java 泛型中的上界(extend)和下界(super)

1、匹配任意类型的通配符
在开发中对象的引用传递(向上向下传递)是最常见的,但是,在泛型的操作中,在进行引用传递的时候泛型类型必须匹配才可以传递,否则不能传递。
例如,如下没有进行泛型类型匹配,一个是String,一个是Object类型。

package Thread1;
class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class demo1{
    public static void main(String args[]){
        Info<String> i = new Info<String>() ;        // 使用String为泛型类型
        i.setVar("MLDN") ;                            // 设置内容
        fun(i) ;                    //把String泛型类型的i对象传递给Object泛型类型的temp。
    }
    public static void fun(Info<Object> temp){        // 接收Object泛型类型的Info对象
        System.out.println("内容:" + temp) ;
    }
};

编译发生错误。

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The method fun(Info<Object>) in the type demo1 is not applicable for the arguments (Info<String>)

    at Thread1.demo1.main(demo1.java:18)

泛型对象进行引用传递的时候,类型必须一致,如果非要传递,则可以将fun方法中Info参数的泛型取消掉(变成 void fun(Info temp))。、
以上确实改进了功能,但是似乎不是很妥当,毕竟之前指定过泛型。
以上程序在fun()方法中使用"Info<?>"的代码形式,表示可以使用任意的泛型类型对象,这样的话fun()方法定义就合理了,但是使用以上方法也有需要注意的地方,
即:如果使用“?“接收泛型对象的时候,则不能设置被泛型指定的内容。

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo14{
    public static void main(String args[]){
        Info<String> i = new Info<String>() ;        // 使用String为泛型类型
        i.setVar("MLDN") ;                            // 设置内容
        fun(i) ;
    }
    public static void fun(Info<?> temp){        // 可以接收任意的泛型对象
        System.out.println("内容:" + temp) ;
    }
};

如果使用”?“意味着可以接收任意的内容,但是此内容无法直接使得用”?“修饰的泛型的对象进行修改。如下就会出问题:

package Thread1;
class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class demo1{
    public static void main(String args[]){
        Info<?> i = new Info<String>() ;        // 使用String为泛型类型
        i.setVar("MLDN") ;                            // 设置内容,这里会出错,因为”?“通配符修饰的对象只能接收,不能修改,也就是不能设置。
    }
};

运行结果:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The method setVar(capture#1-of ?) in the type Info<capture#1-of ?> is not applicable for the arguments (String)

    at Thread1.demo1.main(demo1.java:17)

在使用”?“只能接收,不能修改。
2、受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。

设置上限

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo17{
    public static void main(String args[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象
        Info<Float> i2 = new Info<Float>() ;            // 声明Float的泛型对象
        i1.setVar(30) ;                                    // 设置整数,自动装箱
        i2.setVar(30.1f) ;                                // 设置小数,自动装箱
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? extends Number> temp){    // 只能接收Number及其Number的子类
        System.out.print(temp + "、") ;
    }
};

运行成功。但是,如果传人的泛型类型为String的话就不行,因为String不是Number子类。
在类中使用泛型上限。

package Thread1;
class Info<T extends Number>{    // 此处泛型只能是数字类型
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class demo1{
    public static void main(String args[]){
        Info<Integer> i1 = new Info<Integer>() ;        // 声明Integer的泛型对象
    }
};

如果在使用Info的时候设置成String类型,则编译的时候将会出现错误(String不是Number子类):
设置下限

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo21{
    public static void main(String args[]){
        Info<String> i1 = new Info<String>() ;        // 声明String的泛型对象
        Info<Object> i2 = new Info<Object>() ;        // 声明Object的泛型对象
        i1.setVar("hello") ;
        i2.setVar(new Object()) ;
        fun(i1) ;
        fun(i2) ;
    }
    public static void fun(Info<? super String> temp){    // 只能接收String或Object类型的泛型,String类的父类只有Object类
        System.out.print(temp + "、") ;
    }
};

Object类和String类都是String的父类,所有运行成功,但是如果此时用Integer则会出错,因为integer并不是String父类。
3、解释:泛型与子类继承的限制。
一个类的子类可以通过对象多态性,为其父类实例化,但是在泛型操作中,子类的泛型类型是无法使用父类的泛型类型接收的。例如:Info<String>不能使用Info<Object>接收。
例如,以下肯定出错。

class Info<T>{
    private T var ;        // 定义泛型变量
    public void setVar(T var){
        this.var = var ;
    }
    public T getVar(){
        return this.var ;
    }
    public String toString(){    // 直接打印
        return this.var.toString() ;
    }
};
public class GenericsDemo23{
    public static void main(String args[]){
        Info<String> i1 = new Info<String>() ;        // 泛型类型为String
        Info<Object> i2 = null ;
        i2 = i1 ;                  //这里因为对象泛型类型不同,而出错。
    }
};

代码示例:

import java.util.ArrayList;
import java.util.List;
 
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
 
public class CovariantArrays {
  public static void main(String[] args) {
    //上界
    List<? extends Fruit> flistTop = new ArrayList<Apple>();
    flistTop.add(null);
    //add Fruit对象会报错
    //flist.add(new Fruit());
    Fruit fruit1 = flistTop.get(0);
 
    //下界
    List<? super Apple> flistBottem = new ArrayList<Apple>();
    flistBottem.add(new Apple());
    flistBottem.add(new Jonathan());
    //get Apple对象会报错
    //Apple apple = flistBottem.get(0);
  }
}

这些特点的原因
上界 <? extend Fruit> ,表示所有继承Fruit的子类,但是具体是哪个子类,无法确定,所以调用add的时候,要add什么类型,谁也不知道。但是get的时候,不管是什么子类,不管追溯多少辈,肯定有个父类是Fruit,所以,我都可以用最大的父类Fruit接着,也就是把所有的子类向上转型为Fruit。
下界 <? super Apple>,表示Apple的所有父类,包括Fruit,一直可以追溯到老祖宗Object 。那么当我add的时候,我不能add Apple的父类,因为不能确定List里面存放的到底是哪个父类。但是我可以add Apple及其子类。因为不管我的子类是什么类型,它都可以向上转型为Apple及其所有的父类甚至转型为Object 。但是当我get的时候,Apple的父类这么多,我用什么接着呢,除了Object,其他的都接不住。
所以,归根结底可以用一句话表示,那就是编译器可以支持向上转型,但不支持向下转型。具体来讲,我可以把Apple对象赋值给Fruit的引用,但是如果把Fruit对象赋值给Apple的引用就必须得用cast。
总结:关于上界下界
上界的list只能get,不能add(确切地说不能add出除null之外的对象,包括Object);
下界的list只能add,不能get;
add和get涉及到具体的数据类型了;
add方法是先给预添加的对象创建一个引用,再让这个引用指向具体的父类或子类对象;
get方法是返回具体的类(假设为类型1),必须有一个类型1或类型1的父类引用去指向它;
规定了上界:如果add添加对象,java不知道要为哪一个具体的类添加引用,但java不会自动选择;如果get对象,却可以(人为地)使用上界类创建引用了(因为代码中规定了上界,一看便知)。
规定了下界:如果add添加对象,java不知道要为哪一个具体的类添加引用,这里就可以(人为地)添加下界类的子类对象了(因为代码中规定了下界,一看便知);如果get对象,只能用预获取的类的‘同类’或父类创建引用,但程序员不知道get出来的是哪个类。

posted @ 2021-12-22 17:43  郭慕荣  阅读(3699)  评论(0编辑  收藏  举报