本文共 6057 字,大约阅读时间需要 20 分钟。
有时泛型实例的作用域无法指明具体的参数类型。通配符类型,表示任何类型,通配符类型的符号是“?”,因此通配符类型可应用与所有继承自Object的类上。
示例:
package Generic;import java.util.Date;public class Test{ public static void main(String[] args) { Class c1 = Integer.class; System.out.println(c1.getCanonicalName()); Class c2 = String.class; System.out.println(c2.getCanonicalName()); Class c3 = Date.class; System.out.println(c3.getCanonicalName()); }}
运行结果:
考虑下面问题:现有一个方法用于交换List中的两个元素,由于事先并无法知道List中存在的元素类型,所以将参数类型设置成了含有通配符的实例,如List list。对于该方法来说,并不知道通配符所代表的具体类型,因此,零时存储List中的元素变得困难,因为Java不允许声明通配符常量,即 ? a 这样声明对象是不合法的。所以出现了一种巧妙的解决办法。
示例:
package Generic;import java.util.ArrayList;import java.util.List;public class Tool { publicvoid exchange(List list, int i, int j) { T t = list.get(i); list.set(i, list.get(j)); list.set(j, t); } public static void main(String[] args) { List list = new ArrayList<>(); list.add(3); list.add(5); new Tool().exchange(list, 0, 1); System.out.println(list); }}
运行结果:
边界是指为某一区域划定一个界限,在界限内是允许的,如果超出了界限就不合法Java的泛型边界是指为泛型参数指定范围,在范围内可以合法访问,如果超出了这个界限就是非法访问。Java泛型系统允许使用extends和 super关键字设置边界。前者设定上行边界,即指明参数类型的顶层类,限定实例化泛型类时传入的具体类型,只能是继承自顶层类的。后者设置下行边界,即指定参数类型的底层类,限定传入的参数类型只能是设定类的父类。但设定下行边界时有一定的限制,必须与通配符连用。要为通配符建立一个上行边界: .superclass用作上行边界的类名还可以指定通配符的下行边界: ·只有subclass或其超类接受实参
为什么要定义泛型边界呢?就拿工厂作为例子把,对于Car工厂来说,它能生产的产品只能是Car,如果让它生产Computer,显然就不合理了。对于Car工厂来说还存在下列问题,对于不同品牌的汽车来说,除了有轮子、发动机、座椅零件外还要有品牌标识,这样各种品牌汽车就不同了,因此对于汽车工厂来说,除了能生产通用的汽车之外,还应能生产具体品牌的汽车。如何做呢?Java泛型的边界给我们提供了解决方案。
car.java
package Generic.boundary;public class Car { }
BenzCar.java
package Generic.boundary;public class BenzCar extends Car { public BenzCar() { System.out.println("生产奔驰汽车!"); }}
BMWCar.java
package Generic.boundary;public class BMWCar extends Car { public BMWCar() { System.out.println("生产宝马汽车!"); }}
CarFactory.java
package Generic.boundary;public class CarFactory{ public T create(Class clazz) throws Exception { System.out.println("装载发动机"); System.out.println("装载座椅"); System.out.println("装载轮子"); return clazz.newInstance(); }}
GenericTest.java
package Generic.boundary;public class GenericTest { public static void main(String[] args) throws Exception { CarFactorycarFactory01 = new CarFactory<>(); CarFactory carFactory02 = new CarFactory<>(); System.out.println("开始生产奔驰汽车"); carFactory01.create(BenzCar.class); System.out.println("🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟"); System.out.println("开始生产宝马汽车"); carFactory02.create(BMWCar.class); }}
运行结果:
含边界的的泛型方法与泛型类定义类似,只需要在参数声明参数类型时,使用extends关键字即可,具体的使用方法如下。
示例:
package Generic;public class Sortor { publicT getMax(T x, T y) { if ((int) x > (int) y) { return x; } else return y; } public static void main(String[] args) { System.out.println("泛型方法获取的最大数是: " + new Sortor().getMax(5, 4)); }}
运行结果:
Java泛型还允许为参数类型设置多个边界,设置的方法如下:
示例:
Speakable .java
package Generic.multipleBorders;public interface Speakable { String speak();}
Flyable.java
package Generic.multipleBorders;public interface Flyable { }
Parrot.java
package Generic.multipleBorders;public class Parrot implements Speakable, Flyable { @Override public String speak() { return "我是一只鹦鹉"; }}
Factory.java
package Generic.multipleBorders;public class Factory{ public T create(Class t) throws Exception { return t.newInstance(); }}
FactoryTest.java
package Generic.multipleBorders;public class FactoryTest { public static void main(String[] args) throws Exception { Factoryfactory = new Factory<>(); Parrot parrot = factory.create(Parrot.class); System.out.println(parrot.speak()); }}
运行结果:
这个例子定义了一个多边界的泛型Factory,其表现方式为:Factory<T extends Speakable & Flyable> ,表示传入的参数必须同时继承自Speakable和Flyable
除了可以在泛型类和泛型方法定义时进行边界限定,还可以对泛型实例进行边界限定,对泛型实例的限定需要与通配符一起使用,其使用方法如下:
示例:
Animal .javapackage Generic.wildcardsAndBoundaries;public class Animal { }
Bird.java
package Generic.wildcardsAndBoundaries;public class Bird extends Animal { }
Fish.java
package Generic.wildcardsAndBoundaries;public class Fish extends Animal { }
Zoo.java
package Generic.wildcardsAndBoundaries;public class Zoo{ private T t; public Zoo(T t) { this.t = t; } public T pop() { return this.t; }}
Test.java
package Generic.wildcardsAndBoundaries;public class Test { public static void main(String[] args) { Zoo zoo = new Zoo(new Bird()); zoo = new Zoo (new Fish());// zoo = new Zoo<>(new Integer(2)); 报错 }}
这个例子在实例化参数Zoo时,使用了通配符与extends关键字对参数类型进行限定:Zoo zoo 限定了实例 zoo 中持有的对象只能是Animal的子类注意:创建具体实例时,必须指明具体的参数类型,如 Z00=new Zoo( new Bird()), 这里就不能使用 ,而必须使用像Bird这样具体的参数。
除了可以使用通配符与extends关键子限制参数类型范围以外,还可以super关键连用限定参数类型的范围。其使用方法如下。
package Generic.wildcardsAndBoundaries;public class Test { public static void main(String[] args) { Zoo zoo = new Zoo(new Bird()); zoo = new Zoo (new Animal());// zoo = new Zoo (new Fish()); 会报错 }}
这个例子使用通配符和super 关键字共同为声明的实例限定了参数范围:Zoo zoo,表明实例化时传入的参数只能是 Bird类或其父类。
泛型的继承很容易给人带来一个误区,考虑下面的代码是否合法?
package Generic.wildcardsAndBoundaries;public class Test { public static void main(String[] args) { Zoozoo = new Zoo<>(new Animal()); Zoo birdZoo = new Zoo<>(new Bird());// zoo=birdZoo; 编译器会报错 }}
运行结果: 编译器会报错
直觉可能会告诉你合法,因为Animal是Bird的父类,那么Zoo就是Zoo 的父类,但事实并非如此,当试图 zoo=birdZoo时 编译器会报错。其实Zoo 和Zoo 什么关系都没有,为什么要这样设计呢,因为这是为了确保泛型的安全。
转载地址:http://iakl.baihongyu.com/