泛型

73 阅读2分钟

泛型

泛型实现了参数化类型的概念,使代码可以适用于多种类型。

泛型类

T就是类型参数

public class Holder <T>{
    private T value;

    public Holder(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

泛型接口

public interface Generator <T>{
    T generate();
}

泛型方法

能使用泛型方法解决问题,就要避免整个类的泛化。因为它可以使事情更清楚明白。

public class GenericMethods {
    public <T> void f(T x){
        System.out.println(x.getClass().getName());
    }
    
}

类型擦除

public class GenericHolder<T> {
   private T value;

   public static void main(String[] args) {
       GenericHolder<String> holder = new GenericHolder<String>();
       holder.setValue("hello");
       String s = holder.getValue();
   }

   public T getValue() {
       return value;
   }

   public void setValue(T value) {
       this.value = value;
   }
}

字节码中的类型Object

  public void setValue(T);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #8                  // Field value:Ljava/lang/Object;
         5: return

setValue的时候没有插入类型相关的代码,由编译器确保了类型正确。

最后赋值时,插入了类型类型转换的代码。因为泛型类型被擦除了,实际就是一个Object,需要显示的类型转换。当然这部分转换由编译器帮我们做了。

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class generatic/GenericHolder
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #4                  // String hello
        11: invokevirtual #5                  // Method setValue:(Ljava/lang/Object;)V
        14: aload_1
        15: invokevirtual #6                  // Method getValue:()Ljava/lang/Object;
        18: checkcast     #7                  // class java/lang/String
        21: astore_2
        22: return

类型边界

有时候,我们需要限定类型的范围,就需要用到通配符

List<? extends String> list;
  java.util.List<? extends java.lang.String> list;
    descriptor: Ljava/util/List;
    flags:
    Signature: #13                          // Ljava/util/List<+Ljava/lang/String;>;

1. 协变:<? extends MyClass>

2. 逆变: <? super MyClass>

3. 无界:<?>

说明:

  1. T 和?的区别。T是泛化的类型参数,代表着某一种确切的类型。甚至可以这样使用<? extends T>,?是通配符,代表着某一界限下的所有类型。
  2. 是无界的通配符。似乎和直接声明称Object类型没有啥两样。但是这种显示的声明能够提醒编码和其他协作者,自己是想使用Java泛型来编码的。就像之前写过的RTTI的文章中提到的,声明Class,要比只声明Class好。表示编码者没有忘记类型限定。