java泛型-通配符extends&super详解

10 阅读4分钟

泛型目的

为了更好进行类型指定,在类型确定后,编译器就能发现类型是否使用正常。

泛型的使用

<T> //尖括号 包含泛型类型“TT可以是任意字母

泛型类

package fanxing;

public class Fclass<T>{
    private T something;

    public Fclass(T something) {
        this.something = something;
    }

    public void console(){
        System.out.println("Fclass.console: " + something);
    }

    public static void main(String[] args) {
        //TODO 泛型类使用
        Fclass<String> fclass = new Fclass<>("String");
        fclass.console(); //Fclass.console: String
        // Fclass<String> fclass1 = new Fclass<>(123);//编写代码时编译器就会报错
        Fclass<Integer> fclass1 = new Fclass<>(123);
        fclass1.console(); //Fclass.console: 123
    }
}

泛型方法

泛型方法可以声明在任意类中,是不受类影响的

public class Gen1{ //这是个普通类
	public <T> void show(T t){
        
    }
}

public class Fclass<T>{
    private T something;

    public Fclass(T something) {
        this.something = something;
    }

    public void console(){
        System.out.println("Fclass.console: " + something);
    }

    //泛型方法
    public <T> void show1(T t){
        System.out.println("类的泛型成员变量something:" + something);
        System.out.println("方法参数变量t:" + t);
    }

    public static void main(String[] args) {
        //TODO 泛型类使用
        Fclass<String> fclass = new Fclass<>("String");
        fclass.console(); //Fclass.console: String
        // Fclass<String> fclass1 = new Fclass<>(123);//编写代码时编译器就会报错
        Fclass<Integer> fclass1 = new Fclass<>(123);
        fclass1.console(); //Fclass.console: 123

        Fclass<Integer> fclass2 = new Fclass<>(222);
        /*
          ---------Log------------
          类的泛型成员变量something:222
          方法参数变量t:true
         */
        fclass2.show1(true);
    }
}

类型限定

package fanxing;

import java.lang.reflect.Array;
import java.util.List;

public class FLimit {
    // 这里的 类型T 是实现了Comparable接口的
    public <T extends Comparable> int compare(T t1, T t2){
        return t1.compareTo(t2);
    }

    class Person{

    }
    interface Hand{
        void touch();
    }

    class C1 extends Person implements Hand{

        @Override
        public void touch() {

        }
    }

    // 表示类型T、V 派生于Person并实现了Hand接口的
    // 注意:extends 时如果有类只能放最前面且只能有一个,接口不分顺序用 & 相连
    public <T, V extends Person&Hand> void doSomething(T t1, V v1){
        
    }
}

通配符使用

package fanxing;

class Employee{

}
class Worker extends Employee{

}

public class Pair<T>{
    public static void set(Pair<Employee> employeePair){

    }
    //TODO 方法一:使用泛型方法,泛型限定类型
    public static <E extends Employee> void set1(Pair<E> tPair){

    }
    //TODO 方法二:形参类型使用通配符对泛型类型进行限定
    public static void set2(Pair<? extends Employee> tPair){

    }
    public static void main(String[] args) {
        //employeePair 和 workerPair 不存在继承关系
        Pair<Employee> employeePair = new Pair<>();
        Pair<Worker> workerPair = new Pair<>();
        System.out.println(employeePair.getClass().getName());
        System.out.println(workerPair.getClass().getName());
        System.out.println("Pair.main: "+ (employeePair.getClass() == workerPair.getClass()));
        //TODO 错误示范
        //set(workerPair); //报错,证明类型对不上
        set(employeePair);

        //TODO 方法一:使用泛型方法,泛型限定类型
        set1(workerPair);
        set1(employeePair);

        //TODO 方法二:形参类型使用通配符对泛型类型进行限定
        set2(workerPair);
        set2(employeePair);

        //TODO 推荐方法二,省得麻烦
    }
}

通用代码

class Food {
}
class Fruit extends Food{
}
class Orange extends Fruit{
}
class Apple extends Fruit{
}
class Hongfushi extends Apple {
}
class GenericType<T> {
    private T food;

    public T getFood() {
        return food;
    }

    public void setFood(T food) {
        this.food = food;
    }
}

extends

通配符 extends 关键字,只能做到安全访问数据操作,而无法进行设置。

public static void useExtends(){
    // 写法一:
    GenericType<? extends Fruit> fruitGenericType = new GenericType<Apple>();
    // 写法二:
    GenericType<? extends Fruit> genericType;
    GenericType<Apple> appleGenericType = new GenericType<>();
    GenericType<Hongfushi> hongfushiGenericType = new GenericType<>();
    genericType = appleGenericType;
    genericType = hongfushiGenericType;

    //报错,extends只能进行安全取,
    // 试想一下,编译器怎么知道你设置的是什么水果类型,
    // 由于通配符类型实例genericType 可以是Fruit及其任意子类,
    // 例如:当genericType = appleGenericType 时就是说,类型为苹果,所以进行设置的时候是不是设置的参数为苹果类型的,
    // 同理,genericType 也可以是红富士类型,所以说,这种情况编译器就不知道怎么确定你的类型参数,就无法进行设置。
    //fruitGenericType.setFood(new Apple());

    //如果知道的情况下可以进行类型强转,否则可能引发运行时错误
    Apple apple = (Apple) fruitGenericType.getFood();
    // 正常写法:
    Fruit fruit = fruitGenericType.getFood();
}

由上述代码示例分析可知,由于通配符类型实例genericType 可以是Fruit及其任意子类,例如:

当genericType = appleGenericType 时就是说,类型为苹果,所以进行设置的时候是不是设置的参数为苹果类型的, 同理,genericType 也可以是红富士类型,所以说,这种情况编译器就不知道怎么确定你的类型参数,就无法进行设置。

super

通配符 super,可以对类型参数进行设置,因为知道此类型的下界在哪,所以编译器能知道其类型,同样也可以访问数据

package fanxing;
class Food {
}
class Fruit extends Food{
}
class Orange extends Fruit{
}
class Apple extends Fruit{
}
class Hongfushi extends Fruit {
}
class GenericType<T> {
    private T food;

    public T getFood() {
        return food;
    }

    public void setFood(T food) {
        this.food = food;
    }
}
public class Tongpeifu {

    private static void printSuper(GenericType<? super Apple> p){
        System.out.println( p.getFood().getClass().getName() );
    }
    public static void useSuper(){
        GenericType<Object> objectGenericType = new GenericType<>();
        GenericType<Food> foodGenericType = new GenericType<>();
        GenericType<Fruit> fruitGenericType = new GenericType<>();
        GenericType<Apple> appleGenericType = new GenericType<>();
        GenericType<Hongfushi> hongfushiGenericType = new GenericType<>();
        GenericType<Orange> orangeGenericType = new GenericType<>();

        objectGenericType.setFood(new Apple());
        objectGenericType.setFood(new Fruit());
        printSuper(objectGenericType);


//        printSuper(foodGenericType);

        /**
         * 报错(原因)<br>
         *  以这个水果类型为例,在设置时,是不是必须保证其类型是水果,
         *  而如果类型是食物的话,能否保证它是水果,结果显而是不能的,所以会报错;
         *  但是如果类型是苹果的话,就肯定能保证它属于水果类型
         */
        //fruitGenericType.setFood(new Food()); //上面描述报错原因
        fruitGenericType.setFood(new Fruit());
        fruitGenericType.setFood(new Apple());
        printSuper(fruitGenericType);

//        printSuper(appleGenericType);
        //printSuper(hongfushiGenericType);//报错:由于红富士是苹果的子类,而super限定类型为苹果的父类
        //printSuper(orangeGenericType);//报错:和苹果平级,但是类型不对
    }
    public static void main(String[] args) {
        useSuper();
    }
}

虚拟机泛型实现

Java语言是使用类型擦除的方式来实现的,因为Java类中,依赖Object类进行派生出来的,所以能使用类型擦除来作为泛型的实现方式,并不是真泛型。