java 泛型 上下界通配符

1. 为什么要用通配符和边界?

//T 代表一种类型
//? 是通配符,泛指所有类型

class Fruit {}
class Apple extends Fruit {}

Plate p=new Plate(new Apple());//报错,因为Plate和Plate是容器,没有继承关系
Plate p =new Plate(new Apple());//正常

2.Plate下边界通配符范围

下边界范围

Plate上边界通配符范围

上边界范围

3. 不能往里存,只能往外取

class Plate {
    private T item;

    public Plate(T t) {
        item = t;
    }

    public void set(T t) {
        item = t;
    }

    public T get() {
        return item;
    }
}

Plate p = new Plate(new Apple()); // 实例 p 是协变的

// 不能被存入任何元素
p.set(new Fruit()); // exception
p.set(new Apple()); // exception

// 取出来的东西只能放在 Fruit 或它的基类里
Fruit fruit = p.get();
Object object = p.get();
Apple apple = p.get(); // exception

? extends Fruit,只有在构造函数中可以对 T 类型的 item 进行赋值,通过 set 方法进行赋值是不行的。原因是编译器通过 只知道 p 接受的是 Fruit 及其子类,但是具体是哪个不能确定。但是从写法上,我们在构造时已经显式地指明了 p 的泛型类型是 Apple,为什么编译器还不知道呢?这个涉及到类型擦除。这是因为 JVM 在设计初期就没有考虑过泛型,因此对于 JVM 编译成的字节码来说,也没有泛型的概念,JVM 会使用一个占位符 CAP#1 来表示 p 接受一个 Fruit 或子类,这里就通过 CAP#1 把类型擦除了。所以无论想往 p 插入任何类型都不可以(因为你不能赋值一个 CAP#1 类型)。但是你可以从 p 中往外取 CAP#1,因为 CAP#1 代表的是 Fruit 及其子类,因此往外取时,类型为 Fruit 及其超类就总是安全的。

但是,将 替换为一个具体的类型,set 方法就是生效的。这是因为编译器已经知道 p 只会接受一个确定类型的水果 Apple。

Plate applePlate = new Plate<>(new Apple()); // 实例 applePlate 是不变的
applePlate.set(new Apple());
Apple apple = applePlate.get();
applePlate.set(new GreenApple());

4.下界 不影响往里存,但是往外取只能放在 Object

使用下界 的意思是,Plate 中存放的是任意 Fruit 的基类,但是不确定是哪一个。因此往里放 Fruit 以及其子类一定是可以的(因为这些类一定是 的子类)。但是往外取时就只能是 Object,因为编译器不知道你存的是 Fruit 还是 Meat。Fruit 和 Meat 就只有一个共同父类,那就是 Object。因此往外取 Object 一定是对的。

Plate p = new Plate(new Apple()); // 实例 p 是协变的
p.set(new Fruit());
p.set(new Apple()); 

////读取出来的东西只能存放在Object类里。
Fruit fruit = p.get();
Object object = p.get();//exception
Apple apple = p.get(); // exception

5.>和>的区别

从基本的继承实现概念出发:

若有:class Father implements Comparable
以及:class Son extends Father
则有:Son implements Comparable(子类自动实现父类所实现的接口)
如果定义Son时:Son 没有implements Comparable
那么:对于这种情况:class A >
new A是非法的,
因为Son 没有implements Comparable
而对于这种情况:class A >
new A是合法的,
上述表达式中?可以匹配Father,
因为Son implements Comparable从上可以看出这种写法的好处是父类若实现了Comparable<父>,
那么即使子类没有实现Comparable<子类>,一样可以new A<子类>

6.泛型方法实例

public class MaximumTest
{
   // 比较三个值并返回最大值
   public static > T maximum(T x, T y, T z)
   {                     
      T max = x; // 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; //y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; // 现在 z 更大           
      }
      return max; // 返回最大对象
   }
   public static void main( String args[] )
   {
      System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
                   3, 4, 5, maximum( 3, 4, 5 ) );
 
      System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
                   6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
 
      System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
         "apple", "orange", maximum( "pear", "apple", "orange" ) );
   }
}

你可能感兴趣的:(java 泛型 上下界通配符)