茂名网站开发公司,wordpress修改博客,湖南网络推广服务,网站建设 中企动力烟台我们前面已经讲到了泛型的继承关系#xff1a;PairInteger不是PairNumber的子类。
假设我们定义了PairT#xff1a;
public class PairT { ... }然后#xff0c;我们又针对PairNumber类型写了一个静态方法#xff0c;它接收的参…我们前面已经讲到了泛型的继承关系PairInteger不是PairNumber的子类。
假设我们定义了PairT
public class PairT { ... }然后我们又针对PairNumber类型写了一个静态方法它接收的参数类型是PairNumber
public class PairHelper {static int add(PairNumber p) {Number first p.getFirst();Number last p.getLast();return first.intValue() last.intValue();}
}上述代码是可以正常编译的。使用的时候我们传入
int sum PairHelper.add(new PairNumber(1, 2));注意传入的类型是PairNumber实际参数类型是(Integer, Integer)。
既然实际参数是Integer类型试试传入PairInteger
public class Main {public static void main(String[] args) {PairInteger p new Pair(123, 456);int n add(p);System.out.println(n);}static int add(PairNumber p) {Number first p.getFirst();Number last p.getLast();return first.intValue() last.intValue();}}class PairT {private T first;private T last;public Pair(T first, T last) {this.first first;this.last last;}public T getFirst() {return first;}public T getLast() {return last;}
}直接运行会得到一个编译错误
incompatible types: PairInteger cannot be converted to PairNumber原因很明显因为PairInteger不是PairNumber的子类因此add(PairNumber)不接受参数类型PairInteger。
但是从add()方法的代码可知传入PairInteger是完全符合内部代码的类型规范因为语句
Number first p.getFirst();
Number last p.getLast();实际类型是Integer引用类型是Number没有问题。问题在于方法参数类型定死了只能传入PairNumber。
有没有办法使得方法参数接受PairInteger办法是有的这就是使用Pair? extends Number使得方法接收所有泛型类型为Number或Number子类的Pair类型。我们把代码改写如下
public class Main {public static void main(String[] args) {PairInteger p new Pair(123, 456);int n add(p);System.out.println(n);}static int add(Pair? extends Number p) {Number first p.getFirst();Number last p.getLast();return first.intValue() last.intValue();}}class PairT {private T first;private T last;public Pair(T first, T last) {this.first first;this.last last;}public T getFirst() {return first;}public T getLast() {return last;}
}这样一来给方法传入PairInteger类型时它符合参数Pair? extends Number类型。这种使用? extends Number的泛型定义称之为上界通配符Upper Bounds Wildcards即把泛型类型T的上界限定在Number了。
除了可以传入PairInteger类型我们还可以传入PairDouble类型PairBigDecimal类型等等因为Double和BigDecimal都是Number的子类。
如果我们考察对Pair? extends Number类型调用getFirst()方法实际的方法签名变成了
? extends Number getFirst(); 即返回值是Number或Number的子类因此可以安全赋值给Number类型的变量
Number x p.getFirst(); 然后我们不可预测实际类型就是Integer例如下面的代码是无法通过编译的
Integer x p.getFirst(); 这是因为实际的返回类型可能是Integer也可能是Double或者其他类型编译器只能确定类型一定是Number的子类包括Number类型本身但具体类型无法确定。
我们再来考察一下PairT的set方法
public class Main {public static void main(String[] args) {PairInteger p new Pair(123, 456);int n add(p);System.out.println(n);}static int add(Pair? extends Number p) {Number first p.getFirst();Number last p.getLast();p.setFirst(new Integer(first.intValue() 100));p.setLast(new Integer(last.intValue() 100));return p.getFirst().intValue() p.getFirst().intValue();}}class PairT {private T first;private T last;public Pair(T first, T last) {this.first first;this.last last;}public T getFirst() {return first;}public T getLast() {return last;}public void setFirst(T first) {this.first first;}public void setLast(T last) {this.last last;}
}不出意外我们会得到一个编译错误
incompatible types: Integer cannot be converted to CAP#1
where CAP#1 is a fresh type-variable:CAP#1 extends Number from capture of ? extends Number
编译错误发生在p.setFirst()传入的参数是Integer类型。有些童鞋会问了既然p的定义是Pair? extends Number那么setFirst(? extends Number)为什么不能传入Integer原因还在于擦拭法。如果我们传入的p是PairDouble显然它满足参数定义Pair? extends Number然而PairDouble的setFirst()显然无法接受Integer类型。这就是? extends Number通配符的一个重要限制方法参数签名setFirst(? extends Number)无法传递任何Number的子类型给setFirst(? extends Number)。这里唯一的例外是可以给方法参数传入null
java
p.setFirst(null); // ok, 但是后面会抛出NullPointerException
p.getFirst().intValue(); // NullPointerExceptionextends通配符的作用
如果我们考察Java标准库的java.util.ListT接口它实现的是一个类似“可变数组”的列表主要功能包括
public interface ListT {int size(); // 获取个数T get(int index); // 根据索引获取指定元素void add(T t); // 添加一个新元素void remove(T t); // 删除一个已有元素
}现在让我们定义一个方法来处理列表的每个元素
int sumOfList(List? extends Integer list) {int sum 0;for (int i0; ilist.size(); i) {Integer n list.get(i);sum sum n;}return sum;
}为什么我们定义的方法参数类型是List? extends Integer而不是ListInteger从方法内部代码看传入List? extends Integer或者ListInteger是完全一样的但是注意到List? extends Integer的限制
允许调用get()方法获取Integer的引用 不允许调用set(? extends Integer)方法并传入任何Integer的引用null除外。 因此方法参数类型List? extends Integer表明了该方法内部只会读取List的元素不会修改List的元素因为无法调用add(? extends Integer)、remove(? extends Integer)这些方法。换句话说这是一个对参数List? extends Integer进行只读的方法恶意调用set(null)除外。
使用extends限定T类型
在定义泛型类型PairT的时候也可以使用extends通配符来限定T的类型
public class PairT extends Number { ... }
现在我们只能定义
java
PairNumber p1 null;
PairInteger p2 new Pair(1, 2);
PairDouble p3 null;因为Number、Integer和Double都符合T extends Number。
非Number类型将无法通过编译
PairString p1 null; // compile error!
PairObject p2 null; // compile error!因为String、Object都不符合T extends Number因为它们不是Number类型或Number的子类。
小结
使用类似? extends Number通配符作为方法参数时表示
方法内部可以调用获取Number引用的方法例如Number n obj.getFirst();
方法内部无法调用传入Number引用的方法null除外例如obj.setFirst(Number n);。
即一句话总结使用extends通配符表示可以读不能写。
使用类似T extends Number定义泛型类时表示
泛型类型限定为Number以及Number的子类。