Generics
JDK 1.5 版本开始引入,加强了类型安全(Type safety),强化了语言本身,更加强化了集合框架(Collections Framework)。Java 8 开始加入 Lambda、Stream 更加进一步增强了语言的表达能力。
什么是泛型?
泛型(Generics)意思是参数化类型(Parameterized types)。泛型允许在创建类、接口和方法的时候,以提供参数的形式指定其能操作的数据类型。它的对象是类、接口、方法。这三者都可以泛型化。
在没有泛型特性之前,可以通过 Object 类实现泛型编程,但是不能保证类型安全。
简单的泛型例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 一个简单的泛型例子
// T 是类型参数
// 在创建Gen对象的时候 T 会被实际类型代替。
class Gen<T> {
T ob; // 声明一个类型为 T 的对象
// 给构造器传递一个对类型为 T 的引用
Gen(T o) {
ob = o;
}
// 返回 ob
T getob() {
return ob;
}
// 显示 T 的类型
void showType() {
System.out.println("Type of T is " +
ob.getClass().getName());
}
}
演示操作泛型
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
27
28
29
30
31
32
// 演示使用泛型
class GenDemo {
public static void main(String args[]) {
// 创建一个实际类型为 Integer 的 Gen 引用
Gen<Integer> iOb;
// 创建一个 Gen<Integer> 对象并将其引用赋值给 iOb
// 注意 88 自动装箱成一个 Integer 对象。
iOb = new Gen<Integer>(88);
// 显示 iOb 的数据类型
iOb.showType();
// 获取 iOb 的值。
// 注意不需要类型转换。
int v = iOb.getob();
System.out.println("value: " + v);
System.out.println();
// 创建一个实际类型为 String 的 Gen 对象
Gen<String> strOb = new Gen<String>("Generics Test");
// 显示 strOb 使用的数据类型
strOb.showType();
// 获取 strOb 的值。
// 注意不需要类型转换。
String str = strOb.getob();
System.out.println("value: " + str);
}
}
泛型只适用于引用类型
泛型不能用于基本数据类型 ,只能用于引用类型,因此以下声明是错误的:
Gen<int> intOb = new Gen<int>(53); // 错误,不能使用基本数据类型。
可以使用基本数据类型对应的封装类型,如 Integer
。
泛型因类型参数不同而异
不同类型参数的同一个泛型是不兼容的类型,例如,就上面展示的代码而言,以下这行代码是错误的,不能通过编译:
iOb = strOb; // 错误,不能编译,类型不兼容。
虽然 iOb
和 strOb
都是 Gen<T>
类型,但两者的实际引用类型因为它们的类型参数不同而不同。iOb
实际引用的类型为 Integer
,strOb
实际引用的类型为 String
。所以泛型增加了类型安全,防止错误。
泛型如何增强类型安全
泛型为什么能增强类型安全?上面的代码实际上不使用泛型也可以实现同样的效果,只需要将引用类型声明为 Object
类型,并添加相应的类型转换操作即可。那么使用泛型究竟有什么好处?答案是泛型机制会自动保证与 Gen
相关的所有操作都是类型安全的,无需手动进行类型转换和类型检查。可以看以下没有使用泛型的,实现了同样功能的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// NonGen 功能上等价于 Gen
// 但是没有使用泛型
class NonGen {
Object ob; // ob 现在是 Object 类型
// 给构造器传递一个对类型为 Object 的引用
NonGen(Object o) {
ob = o;
}
// 返回类型为 Object
Object getob() {
return ob;
}
// 显示 ob 的类型
void showType() {
System.out.println("Type of ob is " +
ob.getClass().getName());
}
}
演示没用使用泛型的情况:
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
27
28
29
30
31
32
33
34
35
// 演示没有使用泛型的情况
class NonGenDemo {
public static void main(String args[]) {
NonGen iOb;
// 创建一个 NonGen 对象引用并存放一个 Integer 对象
// 自动装箱
iOb = new NonGen(88);
// 显示 iOb 的数据类型
iOb.showType();
// 获取 iOb 的值
// 这次类型转换是必要的。
int v = (Integer) iOb.getob();
System.out.println("value: " + v);
System.out.println();
// 创建另一个 NonGen 引用并存放一个 String 对象
NonGen strOb = new NonGen("Non-Generics Test");
// 显示 strOb 的数据类型
strOb.showType();
// 获取 strOb 的值
// 再次注意到,类型转换是必要的。
String str = (String) strOb.getob();
System.out.println("value: " + str);
// 可以通过编译,但是概念上来说是错误的!
iOb = strOb;
v = (Integer) iOb.getob(); // 运行时错误!
}
}
不使用泛型的情况,Java 编译器无法知道 NonGen
引用的实际类型,这是很糟糕的,因为:
- 必须要显示地进行类型转换才能获取到存储的数据。
- 很多类型不兼容的错误只能在运行时才能被发现。
总的来说,通过使用泛型,运行时错误被转化成了编译时错误。
含两个参数的泛型
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
27
28
29
30
// A simple generic class with two type
// parameters: T and V.
class TwoGen<T, V> {
T ob1;
V ob2;
// Pass the constructor a reference to
// an object of type T.
TwoGen(T o1, V o2) {
ob1 = o1;
ob2 = o2;
}
// Show types of T and V.
void showTypes() {
System.out.println("Type of T is " +
ob1.getClass().getName());
System.out.println("Type of V is " +
ob2.getClass().getName());
}
T getob1() {
return ob1;
}
V getob2() {
return ob2;
}
}
演示两个类型参数的泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Demonstrate TwoGen.
class SimpGen {
public static void main(String args[]) {
TwoGen<Integer, String> tgObj =
new TwoGen<Integer, String>(88, "Generics");
// Show the types.
tgObj.showTypes();
// Obtain and show values.
int v = tgObj.getob1();
System.out.println("value: " + v);
String str = tgObj.getob2();
System.out.println("value: " + str);
}
}
泛型类的一般形式
class class-name<type-param-list>{ //...}
class-name<type-arg-list> var-name = new class-name<type-arg-list>(cons-arg-list)
范围受限类型
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
// Stats attempts (unsuccessfully) to
// create a generic class that can compute
// the average of an array of numbers of
// any given type.
//
// The class contains an error!
class Stats<T> {
T[] nums; // nums is an array of type T
// Pass the constructor a reference to
// an array of type T.
Stats(T[] o) {
nums = o;
}
// Return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue(); // Error!!!
return sum / nums.length;
}
}
numeric types 才有 doubleValue() 方法。解决上面的问题,可以使用 bounded types,范围受限的类型。语法: <T extends superclass>
修改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// In this version of Stats, the type argument for
// T must be either Number, or a class derived
// from Number.
class Stats<T extends Number> {
T[] nums; // array of Number or subclass
// Pass the constructor a reference to
// an array of type Number or subclass.
Stats(T[] o) {
nums = o;
}
// Return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
}
演示使用受限类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Demonstrate Stats.
class BoundsDemo {
public static void main(String args[]) {
Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new Stats<Integer>(inums);
double v = iob.average();
System.out.println("iob average is " + v);
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new Stats<Double>(dnums);
double w = dob.average();
System.out.println("dob average is " + w);
// This won't compile because String is not a
// subclass of Number.
// String strs[] = { "1", "2", "3", "4", "5" };
// Stats<String> strob = new Stats<String>(strs);
// double x = strob.average();
// System.out.println("strob average is " + v);
}
}
涉及到类或接口,或多个接口的时候,使用 & 操作符将它们连接起来,例如:
class Gen<T extends MyClass & MyInterface> { //... }
使用通配符参数
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
27
28
29
// Use a wildcard.
class Stats<T extends Number> {
T[] nums; // array of Number or subclass
// Pass the constructor a reference to
// an array of type Number or subclass.
Stats(T[] o) {
nums = o;
}
// Return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
// Determine if two averages are the same.
// Notice the use of the wildcard.
boolean sameAvg(Stats<?> ob) {
if(average() == ob.average())
return true;
return false;
}
}
演示使用通配符
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
27
28
29
30
31
32
// Demonstrate wildcard.
class WildcardDemo {
public static void main(String args[]) {
Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new Stats<Integer>(inums);
double v = iob.average();
System.out.println("iob average is " + v);
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new Stats<Double>(dnums);
double w = dob.average();
System.out.println("dob average is " + w);
Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
Stats<Float> fob = new Stats<Float>(fnums);
double x = fob.average();
System.out.println("fob average is " + x);
// See which arrays have same average.
System.out.print("Averages of iob and dob ");
if(iob.sameAvg(dob))
System.out.println("are the same.");
else
System.out.println("differ.");
System.out.print("Averages of iob and fob ");
if(iob.sameAvg(fob))
System.out.println("are the same.");
else
System.out.println("differ.");
}
}
范围受限通配符
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
27
28
29
30
31
32
33
34
35
36
37
38
// Bounded Wildcard arguments.
// Two-dimensional coordinates.
class TwoD {
int x, y;
TwoD(int a, int b) {
x = a;
y = b;
}
}
// Three-dimensional coordinates.
class ThreeD extends TwoD {
int z;
ThreeD(int a, int b, int c) {
super(a, b);
z = c;
}
}
// Four-dimensional coordinates.
class FourD extends ThreeD {
int t;
FourD(int a, int b, int c, int d) {
super(a, b, c);
t = d;
}
}
// This class holds an array of coordinate objects.
class Coords<T extends TwoD> {
T[] coords;
Coords(T[] o) { coords = o; }
}
演示范围受限通配符
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Demonstrate a bounded wildcard.
class BoundedWildcard {
static void showXY(Coords<?> c) {
System.out.println("X Y Coordinates:");
for(int i=0; i < c.coords.length; i++)
System.out.println(c.coords[i].x + " " +
c.coords[i].y);
System.out.println();
}
static void showXYZ(Coords<? extends ThreeD> c) {
System.out.println("X Y Z Coordinates:");
for(int i=0; i < c.coords.length; i++)
System.out.println(c.coords[i].x + " " +
c.coords[i].y + " " +
c.coords[i].z);
System.out.println();
}
static void showAll(Coords<? extends FourD> c) {
System.out.println("X Y Z T Coordinates:");
for(int i=0; i < c.coords.length; i++)
System.out.println(c.coords[i].x + " " +
c.coords[i].y + " " +
c.coords[i].z + " " +
c.coords[i].t);
System.out.println();
}
public static void main(String args[]) {
TwoD td[] = {
new TwoD(0, 0),
new TwoD(7, 9),
new TwoD(18, 4),
new TwoD(-1, -23)
};
Coords<TwoD> tdlocs = new Coords<TwoD>(td);
System.out.println("Contents of tdlocs.");
showXY(tdlocs); // OK, is a TwoD
// showXYZ(tdlocs); // Error, not a ThreeD
// showAll(tdlocs); // Error, not a FourD
// Now, create some FourD objects.
FourD fd[] = {
new FourD(1, 2, 3, 4),
new FourD(6, 8, 14, 8),
new FourD(22, 9, 4, 9),
new FourD(3, -2, -23, 17)
};
Coords<FourD> fdlocs = new Coords<FourD>(fd);
System.out.println("Contents of fdlocs.");
// These are all OK.
showXY(fdlocs);
showXYZ(fdlocs);
showAll(fdlocs);
}
}
创建上限使用以下语法:<? extends superclass>
,创建下限使用这样的语法:<? super subclass>
创建泛型方法
创建一个简单的泛型方法:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Demonstrate a simple generic method.
class GenMethDemo {
// Determine if an object is in an array.
static <T extends Comparable<T>, V extends T> boolean isIn(T x, V[] y) {
for(int i=0; i < y.length; i++)
if(x.equals(y[i])) return true;
return false;
}
public static void main(String args[]) {
// Use isIn() on Integers.
Integer nums[] = { 1, 2, 3, 4, 5 };
if(isIn(2, nums))
System.out.println("2 is in nums");
if(!isIn(7, nums))
System.out.println("7 is not in nums");
System.out.println();
// Use isIn() on Strings.
String strs[] = { "one", "two", "three",
"four", "five" };
if(isIn("two", strs))
System.out.println("two is in strs");
if(!isIn("seven", strs))
System.out.println("seven is not in strs");
// Opps! Won't compile! Types must be compatible.
// if(isIn("two", nums))
// System.out.println("two is in strs");
}
}
泛型方法的类型参数声明放在返回值类型的前面。泛型方法可以是静态的也可以是非静态的。方法可以类型推断,但是仍然可以显式地指明类型参数。
泛型方法的一般形式为: <type-param-list> ret-type meth-name(param-list) { //... }
, type-param-list 为逗号分隔的类型参数。
泛型构造器
构造器可以是泛型,即使它的类不是泛型也可以。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Use a generic constructor.
class GenCons {
private double val;
<T extends Number> GenCons(T arg) {
val = arg.doubleValue();
}
void showval() {
System.out.println("val: " + val);
}
}
class GenConsDemo {
public static void main(String args[]) {
GenCons test = new GenCons(100);
GenCons test2 = new GenCons(123.5F);
test.showval();
test2.showval();
}
}
泛型接口
泛型接口的例子:
1
2
3
4
5
6
7
// A generic interface example.
// A Min/Max interface.
interface MinMax<T extends Comparable<T>> {
T min();
T max();
}
实现泛型接口:
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
// Now, implement MinMax
class MyClass<T extends Comparable<T>> implements MinMax<T> {
T[] vals;
MyClass(T[] o) { vals = o; }
// Return the minimum value in vals.
public T min() {
T v = vals[0];
for(int i=1; i < vals.length; i++)
if(vals[i].compareTo(v) < 0) v = vals[i];
return v;
}
// Return the maximum value in vals.
public T max() {
T v = vals[0];
for(int i=1; i < vals.length; i++)
if(vals[i].compareTo(v) > 0) v = vals[i];
return v;
}
}
演示使用泛型接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class GenIFDemo {
public static void main(String args[]) {
Integer inums[] = {3, 6, 2, 8, 6 };
Character chs[] = {'b', 'r', 'p', 'w' };
MyClass<Integer> iob = new MyClass<Integer>(inums);
MyClass<Character> cob = new MyClass<Character>(chs);
System.out.println("Max value in inums: " + iob.max());
System.out.println("Min value in inums: " + iob.min());
System.out.println("Max value in chs: " + cob.max());
System.out.println("Min value in chs: " + cob.min());
}
}
泛型接口的一般形式:
interface interface-name<type-param-list> { //... }
实现一个泛型接口的情况:
class class-name<type-param-list> implements interface-name<type-arg-list> { //... }
原始类型和老代码
演示原始类型(raw type)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Demonstrate a raw type.
class Gen<T> {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
演示使用原始类型:
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
27
28
29
30
31
32
33
// Demonstrate raw type.
class RawDemo {
public static void main(String args[]) {
// Create a Gen object for Integers.
Gen<Integer> iOb = new Gen<Integer>(88);
// Create a Gen object for Strings.
Gen<String> strOb = new Gen<String>("Generics Test");
// Create a raw-type Gen object and give it
// a Double value.
Gen raw = new Gen(new Double(98.6));
// Cast here is necessary because type is unknown.
double d = (Double) raw.getob();
System.out.println("value: " + d);
// The use of a raw type can lead to run-time.
// exceptions. Here are some examples.
// The following cast causes a run-time error!
// int i = (Integer) raw.getob(); // run-time error
// This assigment overrides type safety.
strOb = raw; // OK, but potentially wrong
// String str = strOb.getob(); // run-time error
// This assingment also overrides type safety.
raw = iOb; // OK, but potentially wrong
// d = (Double) raw.getob(); // run-time error
}
}
原始类型不是类型安全的,因为绕过了泛型提供的类型安全机制。会提示 unchecked warning
泛型类体系结构
泛型与非泛型的继承体系结构之间的区别在于,在泛型继承体系里面,任何类型参数都需要向上传递到超类。这跟构造器参数在继承体系结构中必须向上传递的情况类似。
使用泛型超类
一个简单的泛型继承体系结构
1
2
3
4
5
6
7
8
9
10
11
12
13
// A simple generic class hierarchy.
class Gen<T> {
T ob;
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
Gen 的一个子类
1
2
3
4
5
6
// A subclass of Gen.
class Gen2<T> extends Gen<T> {
Gen2(T o) {
super(o);
}
}
一个子类可以添加它自己的类型参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// A subclass can add its own type parameters.
class Gen<T> {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// A subclass of Gen that defines a second
// type parameter, called V.
class Gen2<T, V> extends Gen<T> {
V ob2;
Gen2(T o, V o2) {
super(o);
ob2 = o2;
}
V getob2() {
return ob2;
}
}
演示继承体系结构
1
2
3
4
5
6
7
8
9
10
11
12
// Create an object of type Gen2.
class HierDemo {
public static void main(String args[]) {
// Create a Gen2 object for String and Integer.
Gen2<String, Integer> x =
new Gen2<String, Integer>("Value is: ", 99);
System.out.print(x.getob());
System.out.println(x.getob2());
}
}
泛型子类
非泛型子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// A nongeneric class can be the superclass
// of a generic subclass.
// A nongeneric class.
class NonGen {
int num;
NonGen(int i) {
num = i;
}
int getnum() {
return num;
}
}
泛型子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// A generic subclass.
class Gen<T> extends NonGen {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o, int i) {
super(i);
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
演示继承体系
1
2
3
4
5
6
7
8
9
10
11
// Create a Gen object.
class HierDemo2 {
public static void main(String args[]) {
// Create a Gen object for String.
Gen<String> w = new Gen<String>("Hello", 47);
System.out.print(w.getob() + " ");
System.out.println(w.getnum());
}
}
泛型体系的运行时类型比较
1
2
3
4
5
6
7
8
9
10
11
12
13
// Use the instanceof operator with a generic class hierarchy.
class Gen<T> {
T ob;
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
Gen 的一个子类
1
2
3
4
5
6
// A subclass of Gen.
class Gen2<T> extends Gen<T> {
Gen2(T o) {
super(o);
}
}
演示泛型运行时类型推断
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Demonstrate run-time type ID implications of generic
// class hierarchy.
class HierDemo3 {
public static void main(String args[]) {
// Create a Gen object for Integers.
Gen<Integer> iOb = new Gen<Integer>(88);
// Create a Gen2 object for Integers.
Gen2<Integer> iOb2 = new Gen2<Integer>(99);
// Create a Gen2 object for Strings.
Gen2<String> strOb2 = new Gen2<String>("Generics Test");
// See if iOb2 is some form of Gen2.
if(iOb2 instanceof Gen2<?>)
System.out.println("iOb2 is instance of Gen2");
// See if iOb2 is some form of Gen.
if(iOb2 instanceof Gen<?>)
System.out.println("iOb2 is instance of Gen");
System.out.println();
// See if strOb2 is a Gen2.
if(strOb2 instanceof Gen2<?>)
System.out.println("strOb is instance of Gen2");
// See if strOb2 is a Gen.
if(strOb2 instanceof Gen<?>)
System.out.println("strOb is instance of Gen");
System.out.println();
// See if iOb is an instance of Gen2, which it is not.
if(iOb instanceof Gen2<?>)
System.out.println("iOb is instance of Gen2");
// See if iOb is an instance of Gen, which it is.
if(iOb instanceof Gen<?>)
System.out.println("iOb is instance of Gen");
// The following can't be compiled because
// generic type info does not exist at run-time.
// if(iOb2 instanceof Gen2<Integer>)
// System.out.println("iOb2 is instance of Gen2<Integer>");
}
}
类型转换
只能是兼容的类型之间转换
重写泛型类的方法
重写一个泛型类中的泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Overriding a generic method in a generic class.
class Gen<T> {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
System.out.print("Gen's getob(): " );
return ob;
}
}
重写了 getob() 的一个 Gen 的子类
1
2
3
4
5
6
7
8
9
10
11
12
13
// A subclass of Gen that overrides getob().
class Gen2<T> extends Gen<T> {
Gen2(T o) {
super(o);
}
// Override getob().
T getob() {
System.out.print("Gen2's getob(): ");
return ob;
}
}
演示如何重写泛型方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Demonstrate generic method override.
class OverrideDemo {
public static void main(String args[]) {
// Create a Gen object for Integers.
Gen<Integer> iOb = new Gen<Integer>(88);
// Create a Gen2 object for Integers.
Gen2<Integer> iOb2 = new Gen2<Integer>(99);
// Create a Gen2 object for Strings.
Gen2<String> strOb2 = new Gen2<String>("Generics Test");
System.out.println(iOb.getob());
System.out.println(iOb2.getob());
System.out.println(strOb2.getob());
}
}
泛型的类型推断
即钻石操作符 diamond operator <>
类型擦除
泛型只是源代码机制,编译后不存在类型参数信息。
桥接方法
有时候编译器需要一个桥接方法,确保子类重写的方法的类型擦除效果与父类方法不同。
模糊错误
常出现在方法重载的时候。
泛型的一些限制
类型参数不可实例化
例如以下代码:
1
2
3
4
5
6
7
8
// 不能创建一个类型为 T 的示例
class Gen<T> {
T ob;
Gen() {
ob = new T(); // 非法操作!
}
}
原因编译器不知道应该创建声明类型的对象,T 只是一个占位符。
静态方法的限制
No static member can use a type parameter declared by the enclosing class. For example, both of the static members of this class are illeagal:
1
2
3
4
5
6
7
8
9
class Wrong<T> {
// Wrong, no static variables of type T.
static T ob;
// Wrong, no static method can use T.
static T getob() {
return ob;
}
}
Although you can’t declare static memebers that use a type parameter declared by the enclosing class, you can declare static generic methods, which define their own type parameters, as was done earlier in this chapter.
泛型在数组的限制
主要包含两方面的限制:不可以实例化元素类型为类型参数的数组。
不可以创建某个特定的泛型的数组引用。以下代码展示这两种情况:
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
27
28
// Generics and arrays.
class Gen<T extends Number> {
T ob;
T vals[]; // OK
Gen(T o, T[] nums) {
ob = o;
// This statement is illegal.
// vals = new T[10]; // can't create an array of T
// But, this statement is OK.
vals = nums; // OK to assign reference to existent array
}
}
class GenArrays {
public static void main(String args[]) {
Integer n[] = { 1, 2, 3, 4, 5 };
Gen<Integer> iOb = new Gen<Integer>(50, n);
// Can't create an array of type-specific generic references.
// Gen<Integer> gens[] = new Gen<Integer>[10]; // Wrong!
// This is OK.
Gen<?> gens[] = new Gen<?>[10]; // OK
}
}
泛型在异常方面的限制
泛型类不可以继承 Throwable
意味着你不可以创建泛型异常类型。