Java从入门到放弃之 泛型

是真要放弃, 因位要学Kotlin, 学Java纯粹就是为了能看懂API和Java库.

泛型在我用过的语言中并不存在, 有必要认真做笔记.

  1. 什么是泛型
    泛型是java的一个语言特性, 使得类型可以作为参数.

  2. 为什么要泛型
    编译期的强类型检查
    不需要类型转换
    泛型算法--使得程序可以处理不同数据类型的集合

  3. 泛型是如何工作
    类型安全
    如果声明List, 编译器在编译期间就会阻止你往list里添加非integer元素.
    类型擦除
    泛型只是一个语法糖, 通过泛型添加的类型信息, 会被编译器从字节码中删除.

用代码解释

不通用的类

class Foo {
  private int t;
  public void set (int t) {
    this.t = t;
  }
  public int get () {
    return this.t;
  }
}

这个类有一个属性t, 显然t的类型只能是int, 这个类无法处理其他的类型.
如果运行:

    Foo o = new Foo();
    o.set("abc");
    //Exception in thread "main" java.lang.Error: Unresolved compilation ...

收获编译器警告.

通用但不安全的类

把Foo的int替换成Object, 由于所有的类型都是Object的子类, Foo成了万金油类.

class Foo {
  private Object t;
  public void set (Object t) {
    this.t = t;
  }
  public Object get () {
    return this.t;
  }
}
...
Foo o = new Foo();
o.set("abc");
o.set(13);

Foo的实例可以接受任何类型的值, 这很方便, 但有安全隐患, 我们不得不手动添加各种类型判断, 因为我们不知道t的类型会是什么.

泛型

使用泛型改写我们的类

class Foo {
  private T t;
  public void set (T t) {
    this.t = t;
  }
  public T get () {
    return this.t;
  }
}

这里我们使用了泛型语法 添加了一个泛型类型T, T叫做 type parameters, 类型参数, 也叫类型变量.

类型变量可以是任何非原始数据类型, 如类, 接口, 数组, 或其他类型变量.
类型变量命名习惯:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

实例化泛型, 将T替换成具体的类型, 传入type argument类型参数.

    Foo o = new Foo();
    // 可以用简便的语法
    // Foo o = new Foo<>();
    o.set("abc");
    o.set(13); // Error

    Foo oo = new Foo();
    oo.set(12);
    oo.set("s"); //Error

这里分别传入StringInteger类型. 这就是泛型的好处, 既有类型检查又有灵活性.

泛型方法

泛型方法引入了自己的类型参数, 静态和构造函数都可以是泛型方法.
泛型方法代码: https://docs.oracle.com/javase/tutorial/java/generics/methods.html

绑定的类型参数 ( bounded type parameters )

绑定的类型参数限制了参数的类型只能是某类型或者某类的子类.

    public  void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

这里的extends 同时表示继承或实现(implements)

通配符

?号是一个通配符, 表示一种未知的类型, 但通配符不会用在方法调用, 实例创建时.

Upper Bounded Wildcards

只限制最高

List // Number 和它子类
List // 只是Number类

Unbounded Wildcards

不限制类型
比如一个方法可以用Object类的功能实现, 或代码并不依赖类型参数时.

List //接受任意类型 

Lower Bounded Wildcards

只限制最低

 List // LBW, 接受Integer和任何Integer的父类
 List // 只是Integer

类型继承关系

List 并不是 List的子类。它们的关系是这样的:

generics-wildcardSubtyping.gif

原图: https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html

通配符使用指南

使用通配符时, 先思考数据的流动方向, 如:

copy(src, dest);

src是数据的流入变量, dest流出变量.

  • 对于流入变量, 使用upper bounded wildcard, 用extends
  • 对于流出变量, 使用lower bounded wildcard, 用super
  • 如果流入变量可以被Object类的方法使用, 使用unbounded wildcard
  • 如果既是流入又是流出变量, 不用通配符.

方法的返回类型, 不使用通配符.

泛型数组

    Object[] foo = new String[2];
    foo[0] = "Hello";
    foo[1] = 2;      //错误

上面的代码会报错, 这是因为数组在运行时也有类型检查, 这说明数组会保留类型信息, 而编译器会移除泛型类型信息, 这种矛盾导致Java不允许给泛型数组赋值.

class GA {
  public T[] foo; // 允许
  public T[] bar = new T[5]; // 错误
}

泛型使用限制

不能定义静态泛型属性, 因静态属性被所有对象共享, 属性的类型将无法确定.

public class 
{
   private static T property; //错误
}

不能做类型转换或执行instanceof
因泛型信息在编译时已被擦除, 所以无法做类型相关的操作.

泛型类无法继承Throwable类, 方法也不能创建或捕获参数化的类型.
类不能包含两个在类型擦除之后有相同签名的重载方法.
不能创建类型参数实例.
不能使用基础数据类型作为泛型参数类型.

你可能感兴趣的:(Java从入门到放弃之 泛型)