java泛型类的继承规则

/ Java / 没有评论 / 1754浏览

首先看一看java泛型类的使用:

/**
 * 一个泛型方法:使程序更加安全
 * 并且能被更多的使用
 * @author 丁**
 *
 * @param <T>
 */
class Pair<T>{
    private T first;
    private T second;
    
    //实例化类型变量
    public static<T> Pair<T> makePair(Class<T> cl){
        try {
            return new Pair<>(cl.newInstance(), cl.newInstance());
        } catch (Exception e) {
            return null;
        } 
    }
    
    public Pair(){ first = null; second = null; }
    public Pair(T first, T second){ this.first = first; this.second = second;}
    
    public T getFirst() {
        return first;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public T getSecond() {
        return second;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
class Father {
    private int age = 40;
    public void getName(){
        System.out.println(age);
    }
}


class Son extends Father{
    private int age = 12;
    public void getName() {
        System.out.println(age);
    }
}

在普通类中:Father aa = new Son();父类是可以用来指向子类的,但是在泛型类中却不是如此:

Pair<Son> bb = new Pair<>();
Pair<Father> cc = bb;//error

1. 虽然Son是Father的子类,但是Pair之间没有继承关系:

List<Object> a1 = new ArrayList<>();
List<String> a2 = new ArrayList<>();
//设a1可以等于a2
//a1 = a2;
//a1.add(1111);因为是对a1进行操作,所以可以添加Object,a1.add(Object ad);
//a2.get(0);报错,因为a1添加Object进入了a2的空间中,但是a2是String类型,所以报错

2. 可以将参数化类型转换成一个原始类型:

List<String> a2 = new ArrayList<>();
List a3 = a2;//可以通过编译,但是后面使用方法时可能会产生类型错误!
//这时候a3对象时原始类型,所以add(Object obj);
a3.add(123);//是对a3进行操作,但是最终结果保存到了a2中,将一个Integer装入String中
显然是错误的;

3. 泛型类可以扩展或实现其他的泛型类

//泛型接口
interface List1<E>{
}

//实现了泛型接口的泛型类
class List2<T, E> implements List1<E>{
    
}

//泛型类
class List3<T>{
    
}

//继承了其他泛型类的泛型类
class List4<T, E> extends List3<E>{
    
}
//因为List2实现了List1
List1<Father> b1 = new List2<Son, Father>();
//List4继承了List3,所以List3是父类,可以指向子类对象。
List3<Father> b2 = new List4<Son, Father>();

虽然这样也完成了泛型类的继承,实现了和普通类一样的多态,但是使用起来并不是特别好,就这样java引入了通配符概念

通配符上限

/*
通配符的上限:Pair<? extends Father> c2
extends是关键字
Pair<T>代表的是某个唯一(具体的泛型类)的泛型类:比如Pair<Son>,Pair<Father>
但是Pair<? extends Father>不是具体的泛型类,它所指的是参数类型为Father的子类的所有泛型类(包括Father)    
*/
Pair<? extends Father> c3;
Pair<Father> c1 = new Pair<>();
Pair<Son> c2 = new Pair<>();
//c2 = c1;error
c3 = c1;
c3 = c2;

需要注意的是:

/*
* 使用通配符的上限的问题: 
* ? extends Father getFirst();
* void setFirst(? extends Father);
* 当c2= c1时:
* c2.setFirst(Father father);时,会将Father对象添加到Son对象内存中,这是不好的
* 所以使用extends上限时,不能使用setFirst(? extends Father),add(? extends Father)* 等方法。
* 但可以使用getFirst();方法
*/
c2.setFirst( new Father(); );//error

通配符下限

/*
* 通配符的下限:
* ? super Son
* 表示的不是某个具体的泛型类,而是表示参数类型为Son的父类的所有可能的泛型类(包括* * Son)
* 和通配符上限一样,通配符的超类型限定不能使用getFirst()方法,但可以使用setFirst(XX)方法
*/
Pair<Son> c1 = new Pair<>();
Pair<? super Son> c3;
c3 = c1;

//另一种超类型限定的写法
Pair<T extends Comparable<? super T>> c4;

无限定通配符:

//无限定通配符
Pair<Son> c7 = new Pair<>();
Pair<String> c5 = new Pair<>();
//c7 = c5;error,因为他们不是同一种类型
Pair<?> c6 = new Pair<>();
c6 = c5;//Pair<?>是所有的Pair泛型类的父类,Pair<?> c6 = new Pair<xx>();

Pair<?>和Pair的本质不同在于:可以用任意的Object对象调用原始的Pair类的setObject()方法;

通配符的捕获:

//交换First,Second变量值
public static void swap(Pair<?> p){
    ? t = p.getFirst();//error,因为通配符(?)不是类型变量,所以不能直接将?写入代码中,利用通配符的捕获来解决这个问题。
    p.setFirst(p.getSecond());
    p.setSecond(t);
}
//交换First,Second变量值
public static void swap(Pair<?> p){
    swapHelper(p);//在调用下面的方法时,类型参数就被捕获了。
}

//利用通配符的捕获来解决该问题
public static <T> void swapHelper(Pair<T> p){
    T t = p.getFirst();//T是具体的某个类型。
    p.setFirst(p.getSecond());
    p.setSecond(t);
}

注意:通配符的捕获只有在许多限制的情况下才是合法的,编译器必须能够确信通配符表达的是单个,确定的类型。