java教程——泛型
Posted 我想月薪过万
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java教程——泛型相关的知识,希望对你有一定的参考价值。
在 java教程——泛型(三)中我们讲到了 泛型的实现原理 和 继承,这一节我们来详细讲一下 extends通配符 的使用。
谈到 extends 这个关键字,大家的第一反应肯定是类的继承。对,有这么个感觉的同学说明你的灵感来了,就在这个时候我们来看一下这么个现象。
“灵异现象”
- 带泛型的类作为参数传递时的问题
package test;
public class changeData {
public static void main(String[] args) {
selfMap<Number> sf = new selfMap<>(1, 2);
System.out.println(add(sf));
}
static int add(selfMap<Number> selfMap){
Number oneValue = selfMap.getOne();
Number twoValue = selfMap.getTwo();
return oneValue.intValue() + twoValue.intValue();
}
}
class selfMap<T>{
private T one;
private T two;
public selfMap(T one, T two) {
this.one = one;
this.two = two;
}
public T getOne(){
return one;
}
public T getTwo(){
return two;
}
}
从运行结果可以看出,运行上面的代码是完全没问题的 。
问题一:将 add() 方法中的两个 Number 改为 Integer?
解:显然编译都通过不了。其实原因很简单,就是一个向下转型的问题,Number 转 Integer,你可知道 Number 的前身是什么,不知道的话你在这瞎转,编译器能打印吗?显然不可以。
问题二:我们往 add() 方法里改传 selfMap<Integer> 可以吗?
解:肯定是不可以的,我们在 java教程——泛型(一)中的 向上转型 的时候说过 selfMap<Integer> 不是 selfMap<Number> 的子类,所以,报错是可想而知的。
当 selfMap<Number> 作为函数参数时,该如何传一个 selfMap<Integer> 呢?
这个时候 extends通配符 就起作用了,我们可以这么写,代码如下:
package test;
import java.lang.reflect.Field;
import java.util.Arrays;
public class changeData {
public static void main(String[] args) {
selfMap<Integer> sf = new selfMap<>(1, 2);
System.out.println(add(sf));
}
static int add(selfMap<? extends Number> selfMap){
Number oneValue = selfMap.getOne();
Number twoValue = selfMap.getTwo();
return oneValue.intValue() + twoValue.intValue();
}
}
class selfMap<T>{
private T one;
private T two;
public selfMap(T one, T two) {
this.one = one;
this.two = two;
}
public T getOne(){
return one;
}
public T getTwo(){
return two;
}
}
你一眼望去可能真看不出有什么区别,可是你细细一看,就会发现我们把 add(selfMap<Number> selfMap) 改成了 add(selfMap<? extends Number> selfMap)
也就是说解决问题的关键在于这么一句申明 <? extends Number> 那它到底是什么呢?
这种使用 <? extends Number> 的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型 T
的上界限定在Number
了。
除了可以传入 Pair<Integer>
类型,我们还可以传入 Pair<Double>
类型,Pair<BigDecimal>
类型等等,因为 Double
和 BigDecimal
都是 Number
的子类。
问题二我们算是解决了,可是问题一我们能解决吗?答案是不可以写成 Number 的子类的,因为你可以传进来的类型太多了,编译器只能确定类型一定是Number
的子类(包括Number
类型本身),但具体类型无法确定。
Set方法的调用
我们把 class selfMap<T> 完善一下,写两个 set 方法玩一下,如下:
class selfMap<T>{
private T one;
private T two;
public selfMap(T one, T two) {
this.one = one;
this.two = two;
}
public T getOne(){
return one;
}
public T getTwo(){
return two;
}
public void setOne(T one) {
this.one = one;
}
public void setTwo(T two) {
this.two = two;
}
}
这个时候我们就可以 调用 里面的 set 方法了,当我们 在 add(selfMap<? extends Number> selfMap) 方法里传入的是 selfMap<? extends Number> selfMap 类型的参数时,根据 “擦试法” 我们可以 明白 真正的 set 方法的写法:
setOne(? extends Number one)
也就是说他需要你传一个 ? extends Number 类型的参数,有人说这个简单啊,不就是 Number 的子类吗?,其实不然,简单来解释还是那个原因:Number子类很多啊,如何保证安全的类型转换呢?很难,所以啊,<? extends Number>
通配符的一个重要限制:方法参数签名 setOne(? extends Number)
无法传递任何Number类型及其
子类型给setOne(? extends Number)
。简单点说就是 :你把 ? extends Number
当做泛型T传入会导致该类的set方法不方便调用。
因此,方法参数类型SelfMap<? extends
Number>
表明了该方法内部只会读取SelfMap
的元素,不会修改SelfMap
的元素(因为无法调用setOne(? extends Number)
方法。换句话说,这是一个对参数SelfMap<? extends
Number>
进行只读的方法(恶意调用setOne(null)
除外)。
使用extends限定T类型
在定义泛型类型Pair<T>
的时候,也可以使用extends
通配符来限定T
的类型:
public class Pair<T extends Number> { ... }
现在,我们只能定义:
Pair<Number> p1 = null;
Pair<Integer> p2 = new Pair<>(1, 2);
Pair<Double> p3 = null;
因为Number
、Integer
和Double
都符合<T extends Number>
。
非Number
类型将无法通过编译:
Pair<String> p1 = null; // compile error!
Pair<Object> 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
的子类。
以上是关于java教程——泛型的主要内容,如果未能解决你的问题,请参考以下文章