是真要放弃, 因位要学Kotlin, 学Java纯粹就是为了能看懂API和Java库.
泛型在我用过的语言中并不存在, 有必要认真做笔记.
什么是泛型
泛型是java的一个语言特性, 使得类型
可以作为参数.为什么要泛型
编译期的强类型检查
不需要类型转换
泛型算法--使得程序可以处理不同数据类型的集合泛型是如何工作
类型安全
如果声明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
这里分别传入String
和Integer
类型. 这就是泛型的好处, 既有类型检查又有灵活性.
泛型方法
泛型方法引入了自己的类型参数, 静态和构造函数都可以是泛型方法.
泛型方法代码: 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 extends Number> // Number 和它子类
List // 只是Number类
Unbounded Wildcards
不限制类型
比如一个方法可以用Object
类的功能实现, 或代码并不依赖类型参数时.
List> //接受任意类型
Lower Bounded Wildcards
只限制最低
List super Integer> // LBW, 接受Integer和任何Integer的父类
List // 只是Integer
类型继承关系
List
并不是 List
的子类。它们的关系是这样的:
原图: 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
类, 方法也不能创建或捕获参数化的类型.
类不能包含两个在类型擦除之后有相同签名的重载方法.
不能创建类型参数实例.
不能使用基础数据类型作为泛型参数类型.