Java泛型

泛型的定义与作用

泛型即参数化类型,而参数概念,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。而参数化类型就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式,然后在调用时传入具体的类型(类型实参)
泛型的本质是为了参数化类型

泛型的优点

1.编译时的强类型检查
泛型要求在声明时指定数据类型,Java编译器在编译时会对泛型代码做强类型检查,并在代码违反类型安全时发出告警。
2.避免了类型转换
未使用泛型:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

使用泛型:

List list = new ArrayList();
list.add("hello");
String s = list.get(0);  

泛型编程可以实现通用算法
通过使用泛型,程序员可以实现通用算法,这些算法可以处理不同类型的集合,可以自定义,并且类型安全且易于阅读

PCES法则:泛型上下边界

泛型擦除原理,泛型桥方法

泛型擦除做了如下工作:
1.把泛型中的所有类型参数替换为Object,如果指定类型边界,则使用类型边界来替换。因此,生成的字节码仅包含普通的类,接口和方法。

2.擦除出现的类型声明,即去掉<>的内容,比如T get()方法,声明就变成了Object get();List就变成了List。如有必要,插入类型转换以保持类型安全。

3.生成桥接方法以保留扩展泛型类型中的多态性。类型擦除确保不为参数化类型创建新类;因此,反省不会产生运行时开销

而对于泛型中的桥方法,举例如下

public class B extends A {
    @Override
    public void setValue(String value) {
        System.out.println("---B.setValue()---");
    }
}

通过上述的泛型的擦除机制,实际A类中的方法应该是这样的

// A 类中的 setValue 方法
public void setValue(Object value){
    this.value = value;
}

这个时候就出现了B类中的setValue方法参数与A类中的setValue方法参数不一样。安装Java重写方法的规则,B类中单setValue方法实际上并没有重写父类中的方法,而是重载。
所以实际上B有两个setValue方法,一个自己的,一个继承来的。
所以在某些场景,比如反射调用B类中的方法的时候,就有可能会调到父类的setValue方法,但是Java编译器处理了这种情况 如下:

public final class com/example/myapplication/test/A extends com/example/myapplication/test/B {


  // access flags 0x1
  public setValue(Ljava/lang/String;)V
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 1
    LDC "t"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 5 L1
    LDC "Not yet implemented"
    ASTORE 2
   L2
    ICONST_0
    ISTORE 3
   L3
    NEW kotlin/NotImplementedError
    DUP
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder. ()V
    LDC "An operation is not implemented: "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKESPECIAL kotlin/NotImplementedError. (Ljava/lang/String;)V
    CHECKCAST java/lang/Throwable
    ATHROW
   L4
    LOCALVARIABLE this Lcom/example/myapplication/test/A; L0 L4 0
    LOCALVARIABLE t Ljava/lang/String; L0 L4 1
    MAXSTACK = 4
    MAXLOCALS = 4

  // access flags 0x1041
  public synthetic bridge setValue(Ljava/lang/Object;)V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST java/lang/String
    INVOKEVIRTUAL com/example/myapplication/test/A.setValue (Ljava/lang/String;)V
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  public ()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
   L1
    LINENUMBER 3 L1
    INVOKESPECIAL com/example/myapplication/test/B. ()V
    RETURN
   L2
    LOCALVARIABLE this Lcom/example/myapplication/test/A; L0 L2 0
    MAXSTACK = 1
    MAXLOCALS = 1

  @Lkotlin/Metadata;(mv={1, 4, 3}, bv={1, 0, 3}, k=1, d1={"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\u0008\u0002\n\u0002\u0010\u0002\n\u0002\u0008\u0002\u0018\u00002\u0008\u0012\u0004\u0012\u00020\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0003J\u0010\u0010\u0004\u001a\u00020\u00052\u0006\u0010\u0006\u001a\u00020\u0002H\u0016\u00a8\u0006\u0007"}, d2={"Lcom/example/myapplication/test/A;", "Lcom/example/myapplication/test/B;", "", "()V", "setValue", "", "t", "app_debug"})
  // compiled from: A.kt
}

会发现其中存在两个setValue,其参数值不一样,其中Object类型的就是Java编译器帮我们生成的桥方法

类型边界

类型边界可以对泛型的类型参数设置限制条件
类型边界的语法形式如下:


示例:

public class GenericsExtendsDemo01 {
    static > T max(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.println(max(3, 4, 5));
        System.out.println(max(6.6, 8.8, 7.7));
        System.out.println(max("pear", "apple", "orange"));
    }
}
// Output:
// 5
// 8.8
// pear

类型边界可以设置多个,语法形式如下:


类型通配符

类型通配符一般是使用?代替具体的类型参数。例如List在逻辑上是List等所有List<具体类型实参>的父类

上界通配符语法形式为
下界通配符语法形式为
上界和下界通配符不能同时使用

无界通配符语法形式为
其有两种应用场景:
1.可以使用Object类中提供的功能来实现的方法
2.使用不依赖于类型参数的泛型类中的方法

你可能感兴趣的:(Java泛型)