泛型类

1
2
3
4
5
6
7
8
public class Test<T> {
private T testname;

private T testfunction(){

}

}

泛型接口

1
2
3
4
5

public interface Test<T> {
public T test(){};

}

需注意点:

  • 在声明类实现泛型接口时,若泛型参数未传入实参,则实现类也需要声明泛型
1
2
3
class MyTest<T> implements Test<T> {
public T one(){};
}
  • 在声明类实现泛型接口时,若泛型参数有传入实参,则实现类里所有使用泛型的地方都需要声明对应的类型
1
2
3
4
class MyTest implements Test<String> {
@override
public String one(){};
}

泛型方法

1
2
3
4

public <T> T test(T name){
System.out.print(name);
}

需注意点:

  • 泛型方法也可以定义在泛型类中,泛型方法里的泛型参数 T 不受泛型类的泛型参数 T 的影响,是独立的。
  • \<T>用来声明该方法为泛型方法,\只是一个代表符号,也可以用\ 或其他( K,V 等)表达。
  • 有了 才可以在泛型方法的参数里声明参数泛型 T ,符号需保持一致,如 对应 E。

泛型通配符

上界通配符

1
<? extends T>

需注意点:

  • 上界通配符只支持从通配类型里 get,而不支持将多种类型的 set 进去, 所以上界描述符Extends适合频繁读取的场景。

原因是:

一个Plate<? extends Fruit>的引用,指向的可能是一个Plate类型的盘子,要往这个盘子里放Banana当然是不被允许的。一个理解是:Plate<? extends Fruit>代表某个只能放某种类型水果的盘子,而不是什么水果都能往里放的盘子

下界通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<? super T>

```

需注意点:

* 下界通配符<? super T>不影响往里面存储,但是读取出来的数据只能是Object类型。

原因是:

下界通配符规定了元素最小的粒度,必须是T及其基类,那么我往里面存储T及其派生类都是可以的,因为它都可以隐式的转化为T类型。但是往外读就不好控制了,里面存储的都是T及其基类,无法转型为任何一种类型,只有Object基类才能装下。

### PECS原则

Effective Java书里的PECS原则。

* 上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。
* 下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景。


### 无线通配符

```java
public class test<?> {

}

泛型的擦除

泛型参数将会被擦除到它的第一个边界(边界可以有多个,重用 extends 关键字,通过它能给与参数类型添加一个边界)。编译器事实上会把类型参数替换为它的第一个边界的类型。如果没有指明边界,那么类型参数将被擦除到Object。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public interface Test {
void f();
}

public class Manipulator<T extends Test> {
T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}

extend关键字后后面的类型信息决定了泛型参数能保留的信息。Java类型擦除只会擦除到 Test 类型。

泛型擦除的缺陷

泛型类型不能显式地运用在运行时类型的操作当中,例如:转型、instanceof 和 new。因为在运行时,所有参数的类型信息都丢失了。类似下面的代码都是无法通过编译的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class Erased<T> {
private final int SIZE = 100;
public static void f(Object arg) {
//编译不通过
if (arg instanceof T) {
}
//编译不通过
T var = new T();
//编译不通过
T[] array = new T[SIZE];
//编译不通过
T[] array = (T) new Object[SIZE];
}
}