一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
在面向对象编程语言中,多态算是一种泛化机制。例如,你可以将方法的参数类型设为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数。这样的方法更加通用一些,可应用的地方也多一些。在类的内部也是如此,凡是需要说明类型的地方,如果都使用基类,确实能够具备更好的灵活性。但是,考虑到除了final类不能扩展,其他任何类都可以被扩展,所以这种灵活性大多数时候也会有一些性能损耗。
有时候,拘泥于单继承体系,也会使程序受限太多。如果方法的参数是一个接口,而不是一个类,这种限制就放松了许多。因为任何实现了该接口的类都能够满足该方法,这也包括暂时还不存在的类。这就给予客户端程序员的一种选择,他可以通过实现一个接口来满足类或方法。因此,接口允许我们快捷地实现类及成果,也使我们有机会创建一个新类来做到这一点。
可是有的时候,即便使用了接口,对程序的约束也还是太强了。因为一旦指明了接口,它就要求你的代码必须使用特定的接口。而我们希望达到的目的是编写更通用的代码,要使代码能够应用于“某种不具体的类型”,而不是一个个具体的接口或类。
这就是Java SE5的重大变化之一:泛型的概念。泛型实现了参数化类型
的概念,使代码可以应用于多种类型。“泛型”这个术语的意思是:“使用于许多许多的类型”。泛型在编程语言中出现时,其最初的目的是希望类或方法具备最广泛的表达能力。如何做到这一点呢,正是通过解耦类或方法所使用的类型之间的约束。
15.1 与C++的比较
15.2 简单泛型
一个只能持有单个对象的类。
1
2
3
4
5
6
7
8
9
|
//: generics/Holder1.java
class Automobile {}
public class Holder1 {
private Automobile a;
public Holder1(Automobile a) { this.a = a; }
Automobile get() { return a; }
} ///:~
|
在Java SE5
之前,可以让这个类直接持有Object
类型的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//: generics/Holder2.java
public class Holder2 {
private Object a;
public Holder2(Object a) { this.a = a; }
public void set(Object a) { this.a = a; }
public Object get() { return a; }
public static void main(String[] args) {
Holder2 h2 = new Holder2(new Automobile());
Automobile a = (Automobile)h2.get();
h2.set("Not an Automobile");
String s = (String)h2.get();
h2.set(1); // Autoboxes to Integer
Integer x = (Integer)h2.get();
}
} ///:~
|
泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。因此,与其使用Object
,我们更喜欢暂时不指定类型,而是稍后再决定具体使用说明类型。要达到这个目的,需要使用类型参数,用尖括号括住,放在类名后面。然后在使用这个类的时候,再用实际的类型替换此类型参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//: generics/Holder3.java
public class Holder3<T> {
private T a;
public Holder3(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
public static void main(String[] args) {
Holder3<Automobile> h3 = new Holder3<Automobile>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("Not an Automobile"); // Error
// h3.set(1); // Error
}
} ///:
|
15.2.1 一个元组类库
1
2
3
4
5
6
7
8
9
10
11
|
//: net/mindview/util/TwoTuple.java
package net.mindview.util;
public class TwoTuple<A,B> {
public final A first;
public final B second;
public TwoTuple(A a, B b) { first = a; second = b; }
public String toString() {
return "(" + first + ", " + second + ")";
}
} ///:~
|
利用继承机制实现长度更长的元组。
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
|
//: net/mindview/util/ThreeTuple.java
package net.mindview.util;
public class ThreeTuple<A,B,C> extends TwoTuple<A,B> {
public final C third;
public ThreeTuple(A a, B b, C c) {
super(a, b);
third = c;
}
public String toString() {
return "(" + first + ", " + second + ", " + third +")";
}
} ///:~
//: net/mindview/util/FourTuple.java
package net.mindview.util;
public class FourTuple<A,B,C,D> extends ThreeTuple<A,B,C> {
public final D fourth;
public FourTuple(A a, B b, C c, D d) {
super(a, b, c);
fourth = d;
}
public String toString() {
return "(" + first + ", " + second + ", " +
third + ", " + fourth + ")";
}
} ///:~
|
为了使用元组,你只需定义一个长度适合的元组,将其作为方法的返回值,然后在return
语句中创建该元组,并返回即可。
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
|
//: generics/TupleTest.java
import net.mindview.util.*;
class Amphibian {}
class Vehicle {}
public class TupleTest {
static TwoTuple<String,Integer> f() {
// Autoboxing converts the int to Integer:
return new TwoTuple<String,Integer>("hi", 47);
}
static ThreeTuple<Amphibian,String,Integer> g() {
return new ThreeTuple<Amphibian, String, Integer>(
new Amphibian(), "hi", 47);
}
static FourTuple<Vehicle,Amphibian,String,Integer> h() {
return new FourTuple<Vehicle,Amphibian,String,Integer>(
new Vehicle(), new Amphibian(), "hi", 47);
}
static FiveTuple<Vehicle,Amphibian,String,Integer,Double> k() {
return new
FiveTuple<Vehicle,Amphibian,String,Integer,Double>(
new Vehicle(), new Amphibian(), "hi", 47, 11.1);
}
public static void main(String[] args) {
TwoTuple<String,Integer> ttsi = f();
System.out.println(ttsi);
// ttsi.first = "there"; // Compile error: final
System.out.println(g());
System.out.println(h());
System.out.println(k());
}
} /* Output: (80% match)
(hi, 47)
([email protected], hi, 47)
([email protected], [email protected], hi, 47)
([email protected], [email protected], hi, 47, 11.1)
*///:~
|
15.2.2 一个堆栈类
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
|
//: generics/LinkedStack.java
// A stack implemented with an internal linked structure.
public class LinkedStack<T> {
//Node类
private static class Node<U> {
U item;
Node<U> next;
Node() { item = null; next = null; }
Node(U item, Node<U> next) {
this.item = item;
this.next = next;
}
boolean end() { return item == null && next == null; }
}
private Node<T> top = new Node<T>(); // End sentinel
public void push(T item) {
top = new Node<T>(item, top);
}
public T pop() {
T result = top.item;
if(!top.end())
top = top.next;
return result;
}
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<String>();
for(String s : "Phasers on stun!".split(" "))
lss.push(s);
String s;
while((s = lss.pop()) != null)
System.out.println(s);
}
} /* Output:
stun!
on
Phasers
*///:~
|
15.2.3 RandomList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/RandomList.java
import java.util.*;
public class RandomList<T> {
private ArrayList<T> storage = new ArrayList<T>();
private Random rand = new Random(47);
public void add(T item) { storage.add(item); }
public T select() {
return storage.get(rand.nextInt(storage.size()));
}
public static void main(String[] args) {
RandomList<String> rs = new RandomList<String>();
for(String s: ("The quick brown fox jumped over " +
"the lazy brown dog").split(" "))
rs.add(s);
for(int i = 0; i < 11; i++)
System.out.print(rs.select() + " ");
}
} /* Output:
brown over fox quick quick dog brown The brown lazy brown
*///:~
|
15.3 泛型接口
泛型也可以应用于接口。
1
2
3
4
|
//: net/mindview/util/Generator.java
// A generic interface.
package net.mindview.util;
public interface Generator<T> { T next(); } ///:~
|
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
|
//: generics/coffee/Coffee.java
package generics.coffee;
public class Coffee {
private static long counter = 0;
private final long id = counter++;
public String toString() {
return getClass().getSimpleName() + " " + id;
}
} ///:~
//: generics/coffee/Latte.java
package generics.coffee;
public class Latte extends Coffee {} ///:~
//: generics/coffee/Mocha.java
package generics.coffee;
public class Mocha extends Coffee {} ///:~
//: generics/coffee/Cappuccino.java
package generics.coffee;
public class Cappuccino extends Coffee {} ///:~
//: generics/coffee/Americano.java
package generics.coffee;
public class Americano extends Coffee {} ///:~
//: generics/coffee/Breve.java
package generics.coffee;
public class Breve extends Coffee {} ///:~
|
编写一个类,实现Generator<Coffee>
接口。
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
|
//: generics/coffee/CoffeeGenerator.java
// Generate different types of Coffee:
package generics.coffee;
import java.util.*;
import net.mindview.util.*;
public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
private Class[] types = { Latte.class, Mocha.class,
Cappuccino.class, Americano.class, Breve.class, };
private static Random rand = new Random(47);
public CoffeeGenerator() {}
// For iteration:
private int size = 0;
public CoffeeGenerator(int sz) { size = sz; }
public Coffee next() {
try { //随机生成一种类型
return (Coffee)types[rand.nextInt(types.length)].newInstance();
// Report programmer errors at run time:
} catch(Exception e) {
throw new RuntimeException(e);
}
}
//迭代器
class CoffeeIterator implements Iterator<Coffee> {
int count = size;
public boolean hasNext() { return count > 0; }
public Coffee next() {
count--;
return CoffeeGenerator.this.next();
}
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
public static void main(String[] args) {
CoffeeGenerator gen = new CoffeeGenerator();
for(int i = 0; i < 5; i++)
System.out.println(gen.next());
for(Coffee c : new CoffeeGenerator(5))
System.out.println(c);
}
} /* Output:
Americano 0
Latte 1
Americano 2
Mocha 3
Mocha 4
Breve 5
Americano 6
Latte 7
Cappuccino 8
Cappuccino 9
*///:~
|
下面的类是Generator<T>
接口的另一个实现,它负责生成Fibonacci
数列:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//: generics/Fibonacci.java
// Generate a Fibonacci sequence.
import net.mindview.util.*;
public class Fibonacci implements Generator<Integer> {
private int count = 0;
public Integer next() { return fib(count++); }
private int fib(int n) { //递归
if(n < 2) return 1;
return fib(n-2) + fib(n-1);
}
public static void main(String[] args) {
Fibonacci gen = new Fibonacci();
for(int i = 0; i < 18; i++)
System.out.print(gen.next() + " ");
}
} /* Output:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*///:~
|
虽然我们在Fibonacci
类使用的都是int
类型,但是其类型参数却是Integer
。这个例子引出了Java
泛型的一个局限性:基本类型无法作为类型参数。不过,Java SE5
具备了自动打包和自动拆包的功能,可以很方便地在基本类型和相应的包装类型之间进行转换。
通过继承来创建适配器类:
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
|
//: generics/IterableFibonacci.java
// Adapt the Fibonacci class to make it Iterable.
import java.util.*;
public class IterableFibonacci
extends Fibonacci implements Iterable<Integer> {
private int n;
public IterableFibonacci(int count) { n = count; }
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
public boolean hasNext() { return n > 0; }
public Integer next() {
n--;
return IterableFibonacci.this.next();
}
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
for(int i : new IterableFibonacci(18))
System.out.print(i + " ");
}
} /* Output:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*///:~
|
15.4 泛型方法
到目前为止,我们看到的泛型,都是应用于整个类上。但同样可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。
如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白。
要定义泛型方法,只需将泛型参数列表置于返回值之前,就像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//: generics/GenericMethods.java
public class GenericMethods {
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);//传入基本类型,自动打包机制就会介入其中,将基本类型的值包装为对应的对象。
gm.f(1.0);
gm.f(1.0F);
gm.f('c');
gm.f(gm);
}
} /* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods
*///:~
|
15.4.1 杠杆利用类型参数推断
创建一个持有List
的Map
1
|
Map<Person,List<? extends Pet>> petPeople = new HashMap<Person,List<? extendsPet>>();
|
编译器本来应该能够从泛型参数列表中的参数推断出另一个参数,编译器暂时还做不到(Java7能做到了)。
编写一个工具类,它包含各种各样的static
方法,专门用来创建各种常用的容器对象:
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
|
//: net/mindview/util/New.java
// Utilities to simplify generic container creation
// by using type argument inference.
package net.mindview.util;
import java.util.*;
public class New {
public static <K,V> Map<K,V> map() {
return new HashMap<K,V>();
}
public static <T> List<T> list() {
return new ArrayList<T>();
}
public static <T> LinkedList<T> lList() {
return new LinkedList<T>();
}
public static <T> Set<T> set() {
return new HashSet<T>();
}
public static <T> Queue<T> queue() {
return new LinkedList<T>();
}
// Examples:
public static void main(String[] args) {
Map<String, List<String>> sls = New.map();
List<String> ls = New.list();
LinkedList<String> lls = New.lList();
Set<String> ss = New.set();
Queue<String> qs = New.queue();
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
|
//: generics/SimplerPets.java
import typeinfo.pets.*;
import java.util.*;
import net.mindview.util.*;
public class SimplerPets {
public static void main(String[] args) {
Map<Person, List<? extends Pet>> petPeople = New.map();
// Rest of the code is the same...
}
} ///:~
|
类型推断只对赋值操作有效,其他时候并不起作用。
1
2
3
4
5
6
7
8
9
10
11
|
//: generics/LimitsOfInference.java
import typeinfo.pets.*;
import java.util.*;
public class LimitsOfInference {
static void
f(Map<Person, List<? extends Pet>> petPeople) {}
public static void main(String[] args) {
// f(New.map()); // Does not compile
}
} ///:~
|
显式的类型说明
在泛型方法中,可以显式地指明类型。
1
2
3
4
5
6
7
8
9
10
11
|
//: generics/ExplicitTypeSpecification.java
import typeinfo.pets.*;
import java.util.*;
import net.mindview.util.*;
public class ExplicitTypeSpecification {
static void f(Map<Person, List<Pet>> petPeople) {}
public static void main(String[] args) {
f(New.<Person, List<Pet>>map());
}
} ///:~
|
15.4.2 可变参数与泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//: generics/GenericVarargs.java
import java.util.*;
public class GenericVarargs {
public static <T> List<T> makeList(T... args) {
List<T> result = new ArrayList<T>();
for(T item : args)
result.add(item);
return result;
}
public static void main(String[] args) {
List<String> ls = makeList("A");
System.out.println(ls);
ls = makeList("A", "B", "C");
System.out.println(ls);
ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
System.out.println(ls);
}
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*///:~
|
15.4.3 用于Generator的泛型方法
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/Generators.java
// A utility to use with Generators.
import generics.coffee.*;
import java.util.*;
import net.mindview.util.*;
public class Generators {
public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen, int n) {
for(int i = 0; i < n; i++)
coll.add(gen.next());
return coll;
}
public static void main(String[] args) {
Collection<Coffee> coffee = fill(new ArrayList<Coffee>(), new CoffeeGenerator(), 4);
for(Coffee c : coffee)
System.out.println(c);
Collection<Integer> fnumbers = fill(
new ArrayList<Integer>(), new Fibonacci(), 12);
for(int i : fnumbers)
System.out.print(i + ", ");
}
} /* Output:
Americano 0
Latte 1
Americano 2
Mocha 3
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,
*///:~
|
15.4.4 一个通用的Generator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: net/mindview/util/BasicGenerator.java
// Automatically create a Generator, given a class
// with a default (no-arg) constructor.
package net.mindview.util;
public class BasicGenerator<T> implements Generator<T> {
private Class<T> type;
public BasicGenerator(Class<T> type){ this.type = type; }
public T next() {
try {
// Assumes type is a public class:
return type.newInstance(); //采用默认构造函数
} catch(Exception e) {
throw new RuntimeException(e);
}
}
// Produce a Default generator given a type token:
public static <T> Generator<T> create(Class<T> type) {
return new BasicGenerator<T>(type);
}
} ///:~
|
1
2
3
4
5
6
7
8
|
//: generics/CountedObject.java
public class CountedObject {
private static long counter = 0;
private final long id = counter++;
public long id() { return id; }
public String toString() { return "CountedObject " + id;}
} ///:~
|
使用BasicGenerator
,可以很容易地位CountedObject
创建一个Generator
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//: generics/BasicGeneratorDemo.java
import net.mindview.util.*;
public class BasicGeneratorDemo {
public static void main(String[] args) {
Generator<CountedObject> gen =
BasicGenerator.create(CountedObject.class);
for(int i = 0; i < 5; i++)
System.out.println(gen.next());
}
} /* Output:
CountedObject 0
CountedObject 1
CountedObject 2
CountedObject 3
CountedObject 4
*///:~
|
15.4.5 简化元组的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//: net/mindview/util/Tuple.java
// Tuple library using type argument inference.
package net.mindview.util;
public class Tuple {
public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
return new TwoTuple<A,B>(a, b);
}
public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b, C c) {
return new ThreeTuple<A,B,C>(a, b, c);
}
public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) {
return new FourTuple<A,B,C,D>(a, b, c, d);
}
public static <A,B,C,D,E> FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) {
return new FiveTuple<A,B,C,D,E>(a, b, c, d, e);
}
} ///:~
|
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
|
//: generics/TupleTest2.java
import net.mindview.util.*;
import static net.mindview.util.Tuple.*;
public class TupleTest2 {
static TwoTuple<String,Integer> f() {
return tuple("hi", 47);
}
static TwoTuple f2() { return tuple("hi", 47); }
static ThreeTuple<Amphibian,String,Integer> g() {
return tuple(new Amphibian(), "hi", 47);
}
static FourTuple<Vehicle,Amphibian,String,Integer> h() {
return tuple(new Vehicle(), new Amphibian(), "hi", 47);
}
static FiveTuple<Vehicle,Amphibian,String,Integer,Double> k() {
return tuple(new Vehicle(), new Amphibian(),
"hi", 47, 11.1);
}
public static void main(String[] args) {
TwoTuple<String,Integer> ttsi = f();
System.out.println(ttsi);
System.out.println(f2());
System.out.println(g());
System.out.println(h());
System.out.println(k());
}
} /* Output: (80% match)
(hi, 47)
(hi, 47)
([email protected], hi, 47)
([email protected], [email protected], hi, 47)
([email protected], [email protected], hi, 47, 11.1)
*///:~
|
15.4.6 一个Set实用工具
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
|
//: net/mindview/util/Sets.java
package net.mindview.util;
import java.util.*;
public class Sets {
//两个集合合并
public static <T> Set<T> union(Set<T> a, Set<T> b) {
Set<T> result = new HashSet<T>(a);
result.addAll(b);
return result;
}//共有部分
public static <T> Set<T> intersection(Set<T> a, Set<T> b) {
Set<T> result = new HashSet<T>(a);
result.retainAll(b);
return result;
}
//移除subset中的元素
// Subtract subset from superset:
public static <T> Set<T> difference(Set<T> superset, Set<T> subset) {
Set<T> result = new HashSet<T>(superset);
result.removeAll(subset);
return result;
}
// Reflexive--everything not in the intersection:
public static <T> Set<T> complement(Set<T> a, Set<T> b) {
return difference(union(a, b), intersection(a, b));
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
|
//: generics/watercolors/Watercolors.java
package generics.watercolors;
public enum Watercolors {
ZINC, LEMON_YELLOW, MEDIUM_YELLOW, DEEP_YELLOW, ORANGE,
BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET,
CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE,
COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE,
SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER,
BURNT_UMBER, PAYNES_GRAY, IVORY_BLACK
} ///:~
|
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
|
//: generics/WatercolorSets.java
import generics.watercolors.*;
import java.util.*;
import static net.mindview.util.Print.*;
import static net.mindview.util.Sets.*;
import static generics.watercolors.Watercolors.*;
public class WatercolorSets {
public static void main(String[] args) {
Set<Watercolors> set1 =
EnumSet.range(BRILLIANT_RED, VIRIDIAN_HUE);
Set<Watercolors> set2 =
EnumSet.range(CERULEAN_BLUE_HUE, BURNT_UMBER);
print("set1: " + set1);
print("set2: " + set2);
print("union(set1, set2): " + union(set1, set2));
Set<Watercolors> subset = intersection(set1, set2);
print("intersection(set1, set2): " + subset);
print("difference(set1, subset): " + difference(set1, subset));
print("difference(set2, subset): " + difference(set2, subset));
print("complement(set1, set2): " + complement(set1, set2));
}
} /* Output: (Sample)
set1: [BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET, CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE]
set2: [CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE, SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER, BURNT_UMBER]
union(set1, set2): [SAP_GREEN, ROSE_MADDER, YELLOW_OCHRE, PERMANENT_GREEN, BURNT_UMBER, COBALT_BLUE_HUE, VIOLET, BRILLIANT_RED, RAW_UMBER, ULTRAMARINE, BURNT_SIENNA, CRIMSON, CERULEAN_BLUE_HUE, PHTHALO_BLUE, MAGENTA, VIRIDIAN_HUE]
intersection(set1, set2): [ULTRAMARINE, PERMANENT_GREEN, COBALT_BLUE_HUE, PHTHALO_BLUE, CERULEAN_BLUE_HUE, VIRIDIAN_HUE]
difference(set1, subset): [ROSE_MADDER, CRIMSON, VIOLET, MAGENTA, BRILLIANT_RED]
difference(set2, subset): [RAW_UMBER, SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, BURNT_UMBER]
complement(set1, set2): [SAP_GREEN, ROSE_MADDER, YELLOW_OCHRE, BURNT_UMBER, VIOLET, BRILLIANT_RED, RAW_UMBER, BURNT_SIENNA, CRIMSON, MAGENTA]
*///:~
|
15.5 匿名内部类
泛型还可以应用于内部类以及匿名内部类。
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
|
//: generics/BankTeller.java
// A very simple bank teller simulation.
import java.util.*;
import net.mindview.util.*;
class Customer {
private static long counter = 1;
private final long id = counter++;
private Customer() {}
public String toString() { return "Customer " + id; }
// A method to produce Generator objects:
public static Generator<Customer> generator() {
return new Generator<Customer>() { //匿名内部类
public Customer next() { return new Customer(); }
};
}
}
class Teller {
private static long counter = 1;
private final long id = counter++;
private Teller() {}
public String toString() { return "Teller " + id; }
// A single Generator object:
public static Generator<Teller> generator = new Generator<Teller>() {
public Teller next() { return new Teller(); }
};
}
public class BankTeller {
public static void serve(Teller t, Customer c) {
System.out.println(t + " serves " + c);
}
public static void main(String[] args) {
Random rand = new Random(47);
Queue<Customer> line = new LinkedList<Customer>();
Generators.fill(line, Customer.generator(), 15);
List<Teller> tellers = new ArrayList<Teller>();
Generators.fill(tellers, Teller.generator, 4);
for(Customer c : line)
serve(tellers.get(rand.nextInt(tellers.size())), c);
}
} /* Output:
Teller 3 serves Customer 1
Teller 2 serves Customer 2
Teller 3 serves Customer 3
Teller 1 serves Customer 4
Teller 1 serves Customer 5
Teller 3 serves Customer 6
Teller 1 serves Customer 7
Teller 2 serves Customer 8
Teller 3 serves Customer 9
Teller 3 serves Customer 10
Teller 2 serves Customer 11
Teller 4 serves Customer 12
Teller 2 serves Customer 13
Teller 1 serves Customer 14
Teller 1 serves Customer 15
*///:~
|
15.6 构建复杂模型
泛型的一个重要好处是能够简单而安全地创建复杂的模型。例如,我们可以很容易地创建List
元组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//: generics/TupleList.java
// Combining generic types to make complex generic types.
import java.util.*;
import net.mindview.util.*;
public class TupleList<A,B,C,D> extends ArrayList<FourTuple<A,B,C,D>> {
public static void main(String[] args) {
TupleList<Vehicle, Amphibian, String, Integer> tl =
new TupleList<Vehicle, Amphibian, String, Integer>();
tl.add(TupleTest.h());
tl.add(TupleTest.h());
for(FourTuple<Vehicle,Amphibian,String,Integer> i: tl)
System.out.println(i);
}
} /* Output: (75% match)
([email protected], [email protected], hi, 47)
([email protected], [email protected], hi, 47)
*///:~
|
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
//: generics/Store.java
// Building up a complex model using generic containers.
import java.util.*;
import net.mindview.util.*;
//商品
class Product {
private final int id;
private String description;
private double price;
public Product(int IDnumber, String descr, double price){
id = IDnumber;
description = descr;
this.price = price;
System.out.println(toString());
}
public String toString() {
return id + ": " + description + ", price: $" + price;
}
public void priceChange(double change) {//更改价格
price += change;
}
public static Generator<Product> generator =
new Generator<Product>() {
private Random rand = new Random(47);
public Product next() {
return new Product(rand.nextInt(1000), "Test",
Math.round(rand.nextDouble() * 1000.0) + 0.99); //商品生成器
}
};
}
//货架
class Shelf extends ArrayList<Product> {
public Shelf(int nProducts) {
Generators.fill(this, Product.generator, nProducts);
}
}
//走廊
class Aisle extends ArrayList<Shelf> {
public Aisle(int nShelves, int nProducts) {//走廊摆放货架
for(int i = 0; i < nShelves; i++)
add(new Shelf(nProducts));
}
}
class CheckoutStand {}
class Office {}
public class Store extends ArrayList<Aisle> {
private ArrayList<CheckoutStand> checkouts = new ArrayList<CheckoutStand>();
private Office office = new Office();
public Store(int nAisles, int nShelves, int nProducts) {
for(int i = 0; i < nAisles; i++)
add(new Aisle(nShelves, nProducts));
}
public String toString() {
StringBuilder result = new StringBuilder();
for(Aisle a : this)
for(Shelf s : a)
for(Product p : s) {
result.append(p);
result.append("\n");
}
return result.toString();
}
public static void main(String[] args) {
System.out.println(new Store(14, 5, 10));
}
} /* Output:
258: Test, price: $400.99
861: Test, price: $160.99
868: Test, price: $417.99
207: Test, price: $268.99
551: Test, price: $114.99
278: Test, price: $804.99
520: Test, price: $554.99
140: Test, price: $530.99
...
*///:~
|
15.7 擦除的神秘之处
1
2
3
4
5
6
7
8
9
10
11
12
|
//: generics/ErasedTypeEquivalence.java
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
} /* Output:
true
*///:~
|
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
|
//: generics/LostInformation.java
import java.util.*;
class Frob {}
class Fnorkle {}
class Quark<Q> {}
class Particle<POSITION,MOMENTUM> {}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<Frob>();
Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
Quark<Fnorkle> quark = new Quark<Fnorkle>();
Particle<Long,Double> p = new Particle<Long,Double>();
System.out.println(Arrays.toString(
list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(
map.getClass().getTypeParameters()));
System.out.println(Arrays.toString(
quark.getClass().getTypeParameters()));
System.out.println(Arrays.toString(
p.getClass().getTypeParameters()));
}
} /* Output:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*///:~
|
Class.getTypeParameters()
将返回一个TypeVariable
对象数组,表示有泛型声明所声明的类型参数。但是,正如你从输出中看到的,你能够发现的只是用作参数占位符的标识符,这并非有用的信息。
在泛型代码内部,无法获得任何有关反省参数类型的信息。
15.7.1 C++的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/Templates.cpp
#include <iostream>
using namespace std;
template<class T> class Manipulator {
T obj;
public:
Manipulator(T x) { obj = x; }
void manipulate() { obj.f(); }
};
class HasF {
public:
void f() { cout << "HasF::f()" << endl; }
};
int main() {
HasF hf;
Manipulator<HasF> manipulator(hf);
manipulator.manipulate();
}
|
当实例化这个模版时,C++编译器将进行检查,因此在Manipulator<HasF>
被实例化的这一刻,它看到HasF
拥有一个方法f()
。如果情况并非如此,就会得到一个编译期错误,这样类型安全就得到了保障。
1
2
3
|
public class HasF {
public void f() { System.out.println("HasF.f()"); }
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//: generics/Manipulation.java
// {CompileTimeError} (Won't compile)
class Manipulator<T> {
private T obj;
public Manipulator(T x) { obj = x; }
// Error: cannot find symbol: method f():
public void manipulate() { obj.f(); }
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> manipulator =
new Manipulator<HasF>(hf);
manipulator.manipulate();
}
} ///:~
|
由于有了擦除,Java
编译器无法将manipulate()
必须能够在obj
上调用f()
这一需求映射到HasF
拥有f()
这一事实上。为了调用f()
,我们必须协助泛型类,给定泛型类的边界,以此告知编译器只能接受遵循这个边界的类型。这里重用了extends
关键字。由于有了边界,便面的代码就可以编译了:
1
2
3
4
5
6
7
|
//: generics/Manipulator2.java
class Manipulator2<T extends HasF> {
private T obj;
public Manipulator2(T x) { obj = x; }
public void manipulate() { obj.f(); }
} ///:~
|
我们说泛型类型参数将擦除到它的第一个边界
,我们还提到了类型参数的擦除
。编译器实际上会把类型参数替换为它的擦除,就像上面的示例一样。T
擦除到了HasF
,就好像在类的声明中用HasF
替换了T
一样。
自己执行擦除,创建出没有泛型的类:
1
2
3
4
5
6
7
|
//: generics/Manipulator3.java
class Manipulator3 {
private HasF obj;
public Manipulator3(HasF x) { obj = x; }
public void manipulate() { obj.f(); }
} ///:~
|
1
2
3
4
5
6
7
|
//: generics/ReturnGenericType.java
class ReturnGenericType<T extends HasF> {
private T obj;
public ReturnGenericType(T x) { obj = x; }
public T get() { return obj; }
} ///:~
|
15.7.2 迁移兼容性
擦除不是一个语言特性。它是Java
泛型实现中的一种折中,因为泛型不是Java
语言出现时就有的组成部分,所以这种折中是必须的。
如果泛型在Java 1.0中就已经是其一部分了,那么这个特性将不会使用擦除来实现。它将使用具体化,使类型参数保持为第一类实体,因此你就能够在类型参数上执行基于类型的语言操作和反射操作。擦除减少了泛型的泛化性。泛型在Java中仍旧是有用的,只是不如它们本来设想的那么有用,而原因就是擦除。
在基于擦除的实现中,泛型类型被当做第二类型处理,即不能在某些重要的上下文环境中使用类型。泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。例如,诸如List<T>
这样的类型注解将被擦除为List
,而普通的类型变量在未指定边界的情况下将被擦除为Object
。
擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这经常被称为“迁移兼容性”。在理想情况下,当所有事物都可以同时被泛化时,我们就可以专注于此。在现实中,即使程序员只编写泛型代码,他们也必须处理在Java SE5之前编写的非泛型类库。那些类库的作者可能从没有想过要泛化他们的代码,或者可能刚刚开始接触泛型。
因此Java泛型不仅必须支持向后兼容性,即现有的代码和类文件仍旧合法,并且继续保持之前的含义;而且还要支持迁移兼容性,使得类库按照它们自己的步调变成泛型的,并且当某个类库变为泛型时,不会破坏依赖于他的代码和应用程序。在决定这就是目标之后,Java设计者们和从事此问题相关工作的各个团队决策认为擦除是唯一可行的解决方法。通过允许非泛型代码与泛型代码啊共存,擦除使得这种向着泛型的迁移成为可能。
15.7.3 擦除的问题
擦除的代价是显著的。泛型不能用于显式地引用运行时类型的操作之中,例如转型
、instanceof
和new表达式
。
另外,擦除和迁移兼容性意味着,使用泛型并不是强制的。
15.7.4 边界处的动作
正是因为有了擦除,我发现泛型最令人困惑的方面源自这样一个事实,即可以表示没有任何意义的事物。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//: generics/ArrayMaker.java
import java.lang.reflect.*;
import java.util.*;
public class ArrayMaker<T> {
private Class<T> kind;
public ArrayMaker(Class<T> kind) { this.kind = kind; }
@SuppressWarnings("unchecked")
T[] create(int size) {
return (T[])Array.newInstance(kind, size);
}
public static void main(String[] args) {
ArrayMaker<String> stringMaker = new ArrayMaker<String>(String.class);
String[] stringArray = stringMaker.create(9);
System.out.println(Arrays.toString(stringArray));
}
} /* Output:
[null, null, null, null, null, null, null, null, null]
*///:~
|
即使kind
被存储为Class<T>
,擦除也意味着它实际将被存储为Class
,没有任何参数。因此,当你在使用它时,例如在创建数组时,Array.newInstance()
实际上并未拥有kind
所蕴含的类型信息,因此这不会产生具体的结果,所以必须转型,这将产生一条令你无法满意的警告。
注意,对于在泛型中创建数组,使用Array.newInstance()
是推荐的方式。
如果我们要创建一个容器而不是数组,情况就有些不同了:
1
2
3
4
5
6
7
8
9
10
|
//: generics/ListMaker.java
import java.util.*;
public class ListMaker<T> {
List<T> create() { return new ArrayList<T>(); }
public static void main(String[] args) {
ListMaker<String> stringMaker= new ListMaker<String>();
List<String> stringList = stringMaker.create();
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//: generics/FilledListMaker.java
import java.util.*;
public class FilledListMaker<T> {
List<T> create(T t, int n) {
List<T> result = new ArrayList<T>();
for(int i = 0; i < n; i++)
result.add(t);
return result;
}
public static void main(String[] args) {
FilledListMaker<String> stringMaker = new FilledListMaker<String>();
List<String> list = stringMaker.create("Hello", 4);
System.out.println(list);
}
} /* Output:
[Hello, Hello, Hello, Hello]
*///:~
|
即使编译器无法知道有关create()
中的T
的任何信息,但是它仍旧可以在编译期确保你放置到result
中的对象具有T
类型,使其适合ArrayList<T>
。因此,即使擦除在方法或类内部移除了有关实际类型的信息,编译器仍旧可以确保在方法或类中使用的类型的内部一致性。
因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界
:即对象进入和离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地点。
1
2
3
4
5
6
7
8
9
10
11
12
|
//: generics/SimpleHolder.java
public class SimpleHolder {
private Object obj;
public void set(Object obj) { this.obj = obj; }
public Object get() { return obj; }
public static void main(String[] args) {
SimpleHolder holder = new SimpleHolder();
holder.set("Item");
String s = (String)holder.get();
}
} ///:~
|
用javap -c SimpleHolder
反编译这个类:
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
|
public class typeinfo.SimpleHolder {
public typeinfo.SimpleHolder();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void set(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field obj:Ljava/lang/Object;
5: return
public java.lang.Object get();
Code:
0: aload_0
1: getfield #2 // Field obj:Ljava/lang/Object;
4: areturn
public static void main(java.lang.String[]);
Code:
0: new #3 // class typeinfo/SimpleHolder
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #5 // String Item
11: invokevirtual #6 // Method set:(Ljava/lang/Object;)V
14: aload_1
15: invokevirtual #7 // Method get:()Ljava/lang/Object;
18: checkcast #8 // class java/lang/String
21: astore_2
22: return
}
|
set()
和get()
方法将直接存储和产生值,而转型是在调用get()
的时候接受检查的。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//: generics/GenericHolder.java
public class GenericHolder<T> {
private T obj;
public void set(T obj) { this.obj = obj; }
public T get() { return obj; }
public static void main(String[] args) {
GenericHolder<String> holder =
new GenericHolder<String>();
holder.set("Item");
String s = holder.get();
}
} ///:~
|
反编译:
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
|
public class typeinfo.GenericHolder<T> {
public typeinfo.GenericHolder();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void set(T);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field obj:Ljava/lang/Object;
5: return
public T get();
Code:
0: aload_0
1: getfield #2 // Field obj:Ljava/lang/Object;
4: areturn
public static void main(java.lang.String[]);
Code:
0: new #3 // class typeinfo/GenericHolder
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #5 // String Item
11: invokevirtual #6 // Method set:(Ljava/lang/Object;)V
14: aload_1
15: invokevirtual #7 // Method get:()Ljava/lang/Object;
18: checkcast #8 // class java/lang/String
21: astore_2
22: return
}
|
所产生的字节码是相同的。对进入set()
的类型进行检查是不需要的,因为这将由编译器执行。而对从get()
返回的值进行转型仍旧是需要的,但这与你自己必须执行的操作是一样的,此处它将由编译器自动插入。
由于所产生的get()
和set()
的字节码相同,所以在泛型中的所有动作都发生在边界处:对传递进来的值进行额外的编译器检查,并插入对传递出去的值的转型。
15.8 擦除的补偿
擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都将无法工作:
1
2
3
4
5
6
7
8
9
10
11
12
|
//: generics/Erased.java
// {CompileTimeError} (Won't compile)
public class Erased<T> {
private final int SIZE = 100;
public static void f(Object arg) {
if(arg instanceof T) {} // Error
T var = new T(); // Error
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[SIZE]; // Unchecked warning
}
} ///:~
|
偶尔可以绕过这些问题来编程,但是有时必须通过引入类型标签来对擦除进行补偿。这意味着你需要显式地传递你的类型的Class
对象,以便你可以在类型表达式中使用它。
例如,在前面示例中对使用instanceof
的尝试最终失败了,因为其类型信息已经被擦除了。如果引入类型标签,就可以转而使用动态的isInstance()
:
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
|
//: generics/ClassTypeCapture.java
class Building {}
class House extends Building {}
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
public static void main(String[] args) {
ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(Building.class);
System.out.println(ctt1.f(new Building()));
System.out.println(ctt1.f(new House()));
ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class);
System.out.println(ctt2.f(new Building()));
System.out.println(ctt2.f(new House()));
}
} /* Output:
true
true
false
true
*///:~
|
15.8.1 创建类型实例
在Erased.java
中对创建一个new T()
的尝试将无法实现,部分原因是因为擦除,而另一部分原因是因为编译器不能验证T
具有默认构造器。但是在C++
中,这种操作很自然,很直观,并且很安全:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//: generics/InstantiateGenericType.cpp
// C++, not Java!
template<class T> class Foo {
T x; // Create a field of type T
T* y; // Pointer to T
public:
// Initialize the pointer:
Foo() { y = new T(); }
};
class Bar {};
int main() {
Foo<Bar> fb;
Foo<int> fi; // ... and it works with primitives
} ///:~
|
Java
中的解决方案是传递一个工厂对象,并使用它来创建新的实例。最便利的工厂对象就是Class
对象,因此如果使用类型标签,那么你就可以使用newInstance()
来创建这个类型的新对象。
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
|
//: generics/InstantiateGenericType.java
import static net.mindview.util.Print.*;
class ClassAsFactory<T> {
T x;
public ClassAsFactory(Class<T> kind) {
try {
x = kind.newInstance();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
class Employee {}
public class InstantiateGenericType {
public static void main(String[] args) {
ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(Employee.class);
print("ClassAsFactory<Employee> succeeded");
try {
ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(Integer.class);
} catch(Exception e) {
print("ClassAsFactory<Integer> failed");
}
}
} /* Output:
ClassAsFactory<Employee> succeeded
ClassAsFactory<Integer> failed
*///:~
|
这可以编译,但是会因ClassAsFactory<Integer>
而失败,因为Integer
没有任何默认的构造器。因为这个错误不是在编译期捕获的,所以Sun
的伙计们对这种方式并不赞成,他们建议使用显式的工厂,并将限制其类型,使得只能接受实现了这个工厂的类:
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
|
//: generics/FactoryConstraint.java
interface FactoryI<T> {
T create();
}
class Foo2<T> {
private T x;
public <F extends FactoryI<T>> Foo2(F factory) {
x = factory.create();
}
// ...
}
class IntegerFactory implements FactoryI<Integer> {
public Integer create() {
return new Integer(0);
}
}
class Widget {//内部类
public static class Factory implements FactoryI<Widget> {
public Widget create() {
return new Widget();
}
}
}
public class FactoryConstraint {
public static void main(String[] args) {
new Foo2<Integer>(new IntegerFactory());
new Foo2<Widget>(new Widget.Factory());
}
} ///:~
|
另一种方式是模版方法
设计模式。
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
|
//: generics/CreatorGeneric.java
abstract class GenericWithCreate<T> {
final T element;
GenericWithCreate() { element = create(); }
abstract T create();
}
class X {}
class Creator extends GenericWithCreate<X> {
X create() { return new X(); }
void f() {
System.out.println(element.getClass().getSimpleName());
}
}
public class CreatorGeneric {
public static void main(String[] args) {
Creator c = new Creator();
c.f();
}
} /* Output:
X
*///:~
|
15.8.2 泛型数组
正如你在Erased.java
中所见,不能创建泛型数组。一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList
:
1
2
3
4
5
6
7
8
|
//: generics/ListOfGenerics.java
import java.util.*;
public class ListOfGenerics<T> {
private List<T> array = new ArrayList<T>();
public void add(T item) { array.add(item); }
public T get(int index) { return array.get(index); }
} ///:~
|
有趣的是,可以按照编译器喜欢的方式来定义一个引用:
1
2
3
4
5
6
7
|
//: generics/ArrayOfGenericReference.java
class Generic<T> {}
public class ArrayOfGenericReference {
static Generic<Integer>[] gia;
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/ArrayOfGeneric.java
public class ArrayOfGeneric {
static final int SIZE = 100;
static Generic<Integer>[] gia;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// 可以将子类转换为父类,但不能讲父类转换为子类 所以这里创建Object数组也是不可以的
// Compiles; produces ClassCastException:
//! gia = (Generic<Integer>[])new Object[SIZE];
// Runtime type is the raw (erased) type:
gia = (Generic<Integer>[])new Generic[SIZE];
System.out.println(gia.getClass().getSimpleName());
gia[0] = new Generic<Integer>();
//! gia[1] = new Object(); // Compile-time error
// Discovers type mismatch at compile time:
//! gia[2] = new Generic<Double>();
}
} /* Output:
Generic[]
*///:~
|
gia
已经被转型为Generic<Integer>[]
,但是这个信息只存在于编译期(并且如果没有@Suppress Warnings注解,你将得到有关这个转型的警告)。在运行时,它仍旧是Object
数组,而这将引发问题。成功创建泛型数组的唯一方式就是创建一个被擦除类型的新数组,然后对其转型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//: generics/GenericArray.java
public class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray(int sz) {
array = (T[])new Object[sz];
}
public void put(int index, T item) {
array[index] = item;
}
public T get(int index) { return array[index]; }
// Method that exposes the underlying representation:
public T[] rep() { return array; }
public static void main(String[] args) {
GenericArray<Integer> gai = new GenericArray<Integer>(10);
// This causes a ClassCastException:
//! Integer[] ia = gai.rep();
// This is OK:
Object[] oa = gai.rep();
}
} ///:~
|
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
|
//: generics/GenericArray2.java
public class GenericArray2<T> {
private Object[] array;
public GenericArray2(int sz) {
array = new Object[sz];
}
public void put(int index, T item) {
array[index] = item;
}
@SuppressWarnings("unchecked")
public T get(int index) { return (T)array[index]; }
@SuppressWarnings("unchecked")
public T[] rep() {
return (T[])array; // Warning: unchecked cast
}
public static void main(String[] args) {
GenericArray2<Integer> gai = new GenericArray2<Integer>(10);
for(int i = 0; i < 10; i ++)
gai.put(i, i);
for(int i = 0; i < 10; i ++)
System.out.print(gai.get(i) + " ");
System.out.println();
try {
Integer[] ia = gai.rep();
} catch(Exception e) { System.out.println(e); }
}
} /* Output: (Sample)
0 1 2 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
*///:~
|
不能声明T[] array = new T[sz]
,因此我们创建了一个对象数组,然后将其转型。
rep()
方法将返回T[]
,它在main()
中将用于gai
,因此应该是Integer[]
,但是如果调用它,并尝试着将结果作为Integer[]
引用来捕获,就会得到ClassCastException
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/GenericArrayWithTypeToken.java
import java.lang.reflect.*;
public class GenericArrayWithTypeToken<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArrayWithTypeToken(Class<T> type, int sz) {
array = (T[])Array.newInstance(type, sz);
}
public void put(int index, T item) {
array[index] = item;
}
public T get(int index) { return array[index]; }
// Expose the underlying representation:
public T[] rep() { return array; }
public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
// This now works:
Integer[] ia = gai.rep();
}
} ///:~
|
15.9 边界
因为擦除移除了类型信息,所以,无界泛型参数调用的方法只是那些可以用Object
调用的方法。但是,如果能够将这个参数限制为某个类型自己,那么就可以用这些类型子集来调用方法。为了执行这种限制,Java
泛型重用了extends
关键字。
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
|
//: generics/BasicBounds.java
interface HasColor { java.awt.Color getColor(); }
class Colored<T extends HasColor> {
T item;
Colored(T item) { this.item = item; }
T getItem() { return item; }
// The bound allows you to call a method:
java.awt.Color color() { return item.getColor(); }
}
class Dimension { public int x, y, z; }
//类必须在前
// This won't work -- class must be first, then interfaces:
// class ColoredDimension<T extends HasColor & Dimension> {
// Multiple bounds:
class ColoredDimension<T extends Dimension & HasColor> {
T item;
ColoredDimension(T item) { this.item = item; }
T getItem() { return item; }
java.awt.Color color() { return item.getColor(); }
int getX() { return item.x; }
int getY() { return item.y; }
int getZ() { return item.z; }
}
interface Weight { int weight(); }
// As with inheritance, you can have only one
// concrete class but multiple interfaces:
class Solid<T extends Dimension & HasColor & Weight> {
T item;
Solid(T item) { this.item = item; }
T getItem() { return item; }
java.awt.Color color() { return item.getColor(); }
int getX() { return item.x; }
int getY() { return item.y; }
int getZ() { return item.z; }
int weight() { return item.weight(); }
}
class Bounded extends Dimension implements HasColor, Weight {
public java.awt.Color getColor() { return null; }
public int weight() { return 0; }
}
public class BasicBounds {
public static void main(String[] args) {
Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
solid.color();
solid.getY();
solid.weight();
}
} ///:~
|
继承的每个层次上添加边界限制:
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
|
//: generics/InheritBounds.java
class HoldItem<T> {
T item;
HoldItem(T item) { this.item = item; }
T getItem() { return item; }
}
class Colored2<T extends HasColor> extends HoldItem<T> {
Colored2(T item) { super(item); }
java.awt.Color color() { return item.getColor(); }
}
class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> {
ColoredDimension2(T item) { super(item); }
int getX() { return item.x; }
int getY() { return item.y; }
int getZ() { return item.z; }
}
class Solid2<T extends Dimension & HasColor & Weight> extends ColoredDimension2<T> {
Solid2(T item) { super(item); }
int weight() { return item.weight(); }
}
public class InheritBounds {
public static void main(String[] args) {
Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded());
solid2.color();
solid2.getY();
solid2.weight();
}
} ///:~
|
具有更多层次的示例:
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
|
//: generics/EpicBattle.java
// Demonstrating bounds in Java generics.
import java.util.*;
interface SuperPower {}
interface XRayVision extends SuperPower {
void seeThroughWalls();
}
interface SuperHearing extends SuperPower {
void hearSubtleNoises();
}
interface SuperSmell extends SuperPower {
void trackBySmell();
}
class SuperHero<POWER extends SuperPower> {
POWER power;
SuperHero(POWER power) { this.power = power; }
POWER getPower() { return power; }
}
class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {
SuperSleuth(POWER power) { super(power); }
void see() { power.seeThroughWalls(); }
}
class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> {
CanineHero(POWER power) { super(power); }
void hear() { power.hearSubtleNoises(); }
void smell() { power.trackBySmell(); }
}
class SuperHearSmell implements SuperHearing, SuperSmell {
public void hearSubtleNoises() {}
public void trackBySmell() {}
}
class DogBoy extends CanineHero<SuperHearSmell> {
DogBoy() { super(new SuperHearSmell()); }
}
public class EpicBattle {
// Bounds in generic methods:
static <POWER extends SuperHearing> void useSuperHearing(SuperHero<POWER> hero) {
hero.getPower().hearSubtleNoises();
}
static <POWER extends SuperHearing & SuperSmell> void superFind(SuperHero<POWER> hero) {
hero.getPower().hearSubtleNoises();
hero.getPower().trackBySmell();
}
public static void main(String[] args) {
DogBoy dogBoy = new DogBoy();
useSuperHearing(dogBoy);
superFind(dogBoy);
// You can do this:
List<? extends SuperHearing> audioBoys;
// But you can't do this:
// List<? extends SuperHearing & SuperSmell> dogBoys;
}
} ///:~
|
15.10 通配符
可以向导出类型的数组赋予基类型的数组引用:
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
|
//: generics/CovariantArrays.java
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class CovariantArrays {
public static void main(String[] args) {
Fruit[] fruit = new Apple[10];
fruit[0] = new Apple(); // OK
fruit[1] = new Jonathan(); // OK
// Runtime type is Apple[], not Fruit[] or Orange[]:
try {
// Compiler allows you to add Fruit:
fruit[0] = new Fruit(); // ArrayStoreException
} catch(Exception e) { System.out.println(e); }
try {
// Compiler allows you to add Oranges:
fruit[0] = new Orange(); // ArrayStoreException
} catch(Exception e) { System.out.println(e); }
}
} /* Output:
java.lang.ArrayStoreException: Fruit
java.lang.ArrayStoreException: Orange
*///:~
|
1
2
3
4
5
6
7
8
|
//: generics/NonCovariantGenerics.java
// {CompileTimeError} (Won't compile)
import java.util.*;
public class NonCovariantGenerics {
// Compile Error: incompatible types:
List<Fruit> flist = new ArrayList<Apple>();
} ///:~
|
有时你想要在两个类之间建立某种类型的向上转型关系,这正是通配符所允许的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//: generics/GenericsAndCovariance.java
import java.util.*;
public class GenericsAndCovariance {
public static void main(String[] args) {
// Wildcards allow covariance:
List<? extends Fruit> flist = new ArrayList<Apple>();
// Compile Error: can't add any type of object:
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // Legal but uninteresting
// We know that it returns at least Fruit:
Fruit f = flist.get(0);
}
} ///:~
|
15.10.1 编译器有多聪明
1
2
3
4
5
6
7
8
9
10
11
12
|
//: generics/CompilerIntelligence.java
import java.util.*;
public class CompilerIntelligence {
public static void main(String[] args) {
List<? extends Fruit> flist =
Arrays.asList(new Apple());
Apple a = (Apple)flist.get(0); // No warning
flist.contains(new Apple()); // Argument is 'Object'
flist.indexOf(new Apple()); // Argument is 'Object'
}
} ///:~
|
当你指定一个ArrayList<? extends Fruit>
时,add()
的参数就变成了“? extends Fruit”。从这个描述中,编译器并不能了解这里需要Fruit
的哪个具体子类型,因此它不会接受任何类型的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
27
28
29
|
//: generics/Holder.java
public class Holder<T> {
private T value;
public Holder() {}
public Holder(T val) { value = val; }
public void set(T val) { value = val; }
public T get() { return value; }
public boolean equals(Object obj) {
return value.equals(obj);
}
public static void main(String[] args) {
Holder<Apple> Apple = new Holder<Apple>(new Apple());
Apple d = Apple.get();
Apple.set(d);
// Holder<Fruit> Fruit = Apple; // Cannot upcast
Holder<? extends Fruit> fruit = Apple; // OK
Fruit p = fruit.get();
d = (Apple)fruit.get(); // Returns 'Object'
try {
Orange c = (Orange)fruit.get(); // No warning
} catch(Exception e) { System.out.println(e); }
// fruit.set(new Apple()); // Cannot call set()
// fruit.set(new Fruit()); // Cannot call set()
System.out.println(fruit.equals(d)); // OK
}
} /* Output: (Sample)
java.lang.ClassCastException: Apple cannot be cast to Orange
true
*///:~
|
15.10.2 逆变
还可以走另外一条路,即使用超类型通配符
。
1
2
3
4
5
6
7
8
9
10
|
//: generics/SuperTypeWildcards.java
import java.util.*;
public class SuperTypeWildcards {
static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
// apples.add(new Fruit()); // Error
}
} ///:~
|
List<? super Apple>是Apple的某种基类型的List,这样你就知道向其中添加Apple或Apple的子类型是安全的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//: generics/GenericWriting.java
import java.util.*;
public class GenericWriting {
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static List<Apple> apples = new ArrayList<Apple>();
static List<Fruit> fruit = new ArrayList<Fruit>();
static void f1() {
writeExact(apples, new Apple());
// writeExact(fruit, new Apple()); // Error:
// Incompatible types: found Fruit, required Apple
}
static <T> void writeWithWildcard(List<? super T> list, T item) {
list.add(item);
}
static void f2() {
writeWithWildcard(apples, new Apple());
writeWithWildcard(fruit, new Apple());
}
public static void main(String[] args) { f1(); f2(); }
} ///:~
|
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
|
//: generics/GenericReading.java
import java.util.*;
public class GenericReading {
static <T> T readExact(List<T> list) {
return list.get(0);
}
static List<Apple> apples = Arrays.asList(new Apple());
static List<Fruit> fruit = Arrays.asList(new Fruit());
// A static method adapts to each call:
static void f1() {
Apple a = readExact(apples);
Fruit f = readExact(fruit);
f = readExact(apples);
}
// If, however, you have a class, then its type is
// established when the class is instantiated:
static class Reader<T> {
T readExact(List<T> list) { return list.get(0); }
}
static void f2() {
Reader<Fruit> fruitReader = new Reader<Fruit>();
Fruit f = fruitReader.readExact(fruit);
// Fruit a = fruitReader.readExact(apples); // Error:
// readExact(List<Fruit>) cannot be
// applied to (List<Apple>).
}
static class CovariantReader<T> {
T readCovariant(List<? extends T> list) {
return list.get(0);
}
}
static void f3() {
CovariantReader<Fruit> fruitReader =
new CovariantReader<Fruit>();
Fruit f = fruitReader.readCovariant(fruit);
Fruit a = fruitReader.readCovariant(apples);
}
public static void main(String[] args) {
f1(); f2(); f3();
}
} ///:~
|
15.10.3 无界通配符
无界通配符<?>
看起来意味着“任何事物”,因此使用无界通配符好像等价于使用原生类型。
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
|
//: generics/UnboundedWildcards1.java
import java.util.*;
public class UnboundedWildcards1 {
static List list1;
static List<?> list2;
static List<? extends Object> list3;
static void assign1(List list) {
list1 = list;
list2 = list;
// list3 = list; // Warning: unchecked conversion
// Found: List, Required: List<? extends Object>
}
static void assign2(List<?> list) {
list1 = list;
list2 = list;
list3 = list;
}
static void assign3(List<? extends Object> list) {
list1 = list;
list2 = list;
list3 = list;
}
public static void main(String[] args) {
assign1(new ArrayList());
assign2(new ArrayList());
// assign3(new ArrayList()); // Warning:
// Unchecked conversion. Found: ArrayList
// Required: List<? extends Object>
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
// Both forms are acceptable as List<?>:
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/UnboundedWildcards2.java
import java.util.*;
public class UnboundedWildcards2 {
static Map map1;
static Map<?,?> map2;
static Map<String,?> map3;
static void assign1(Map map) { map1 = map; }
static void assign2(Map<?,?> map) { map2 = map; }
static void assign3(Map<String,?> map) { map3 = map; }
public static void main(String[] args) {
assign1(new HashMap());
assign2(new HashMap());
// assign3(new HashMap()); // Warning:
// Unchecked conversion. Found: HashMap
// Required: Map<String,?>
assign1(new HashMap<String,Integer>());
assign2(new HashMap<String,Integer>());
assign3(new HashMap<String,Integer>());
}
} ///:~
|
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
//: generics/Wildcards.java
// Exploring the meaning of wildcards.
public class Wildcards {
// Raw argument:
static void rawArgs(Holder holder, Object arg) {
// holder.set(arg); // Warning:
// Unchecked call to set(T) as a
// member of the raw type Holder
// holder.set(new Wildcards()); // Same warning
// Can't do this; don't have any 'T':
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
// Similar to rawArgs(), but errors instead of warnings:
static void unboundedArg(Holder<?> holder, Object arg) {
// holder.set(arg); // Error:
// set(capture of ?) in Holder<capture of ?>
// cannot be applied to (Object)
// holder.set(new Wildcards()); // Same error
// Can't do this; don't have any 'T':
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
static <T> T exact1(Holder<T> holder) {
T t = holder.get();
return t;
}
static <T> T exact2(Holder<T> holder, T arg) {
holder.set(arg);
T t = holder.get();
return t;
}
static <T> T wildSubtype(Holder<? extends T> holder, T arg) {
// holder.set(arg); // Error:
// set(capture of ? extends T) in
// Holder<capture of ? extends T>
// cannot be applied to (T)
T t = holder.get();
return t;
}
static <T> void wildSupertype(Holder<? super T> holder, T arg) {
holder.set(arg);
// T t = holder.get(); // Error:
// Incompatible types: found Object, required T
// OK, but type information has been lost:
Object obj = holder.get();
}
public static void main(String[] args) {
Holder raw = new Holder<Long>();
// Or:
raw = new Holder();
Holder<Long> qualified = new Holder<Long>();
Holder<?> unbounded = new Holder<Long>();
Holder<? extends Long> bounded = new Holder<Long>();
Long lng = 1L;
rawArgs(raw, lng);
rawArgs(qualified, lng);
rawArgs(unbounded, lng);
rawArgs(bounded, lng);
unboundedArg(raw, lng);
unboundedArg(qualified, lng);
unboundedArg(unbounded, lng);
unboundedArg(bounded, lng);
// Object r1 = exact1(raw); // Warnings:
// Unchecked conversion from Holder to Holder<T>
// Unchecked method invocation: exact1(Holder<T>)
// is applied to (Holder)
Long r2 = exact1(qualified);
Object r3 = exact1(unbounded); // Must return Object
Long r4 = exact1(bounded);
// Long r5 = exact2(raw, lng); // Warnings:
// Unchecked conversion from Holder to Holder<Long>
// Unchecked method invocation: exact2(Holder<T>,T)
// is applied to (Holder,Long)
Long r6 = exact2(qualified, lng);
// Long r7 = exact2(unbounded, lng); // Error:
// exact2(Holder<T>,T) cannot be applied to
// (Holder<capture of ?>,Long)
// Long r8 = exact2(bounded, lng); // Error:
// exact2(Holder<T>,T) cannot be applied
// to (Holder<capture of ? extends Long>,Long)
// Long r9 = wildSubtype(raw, lng); // Warnings:
// Unchecked conversion from Holder
// to Holder<? extends Long>
// Unchecked method invocation:
// wildSubtype(Holder<? extends T>,T) is
// applied to (Holder,Long)
Long r10 = wildSubtype(qualified, lng);
// OK, but can only return Object:
Object r11 = wildSubtype(unbounded, lng);
Long r12 = wildSubtype(bounded, lng);
// wildSupertype(raw, lng); // Warnings:
// Unchecked conversion from Holder
// to Holder<? super Long>
// Unchecked method invocation:
// wildSupertype(Holder<? super T>,T)
// is applied to (Holder,Long)
wildSupertype(qualified, lng);
// wildSupertype(unbounded, lng); // Error:
// wildSupertype(Holder<? super T>,T) cannot be
// applied to (Holder<capture of ?>,Long)
// wildSupertype(bounded, lng); // Error:
// wildSupertype(Holder<? super T>,T) cannot be
// applied to (Holder<capture of ? extends Long>,Long)
}
} ///:~
|
在rawArgs()
中,编译器知道Holder是一个泛型雷兴国,因此即使它在这里被表示成功一个原生类型,编译器仍旧知道向set()
传递一个Object是不安全的。由于它是原生类型,你可以将任何类型的对象传递给set()
,而这个对象将被向上转型为Object。因此,无论何时,只要使用了原生类型,都会放弃编译期检查。对get()的调用说明了相同的问题:没有任何T类型的对象,因此结果只能是一个Object。
人们很自然地会开始考虑原生Holder与Holder<?>是大致相同的事物。但是unboundedArg()强调tamen
15.10.4 捕获转换
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
|
//: generics/CaptureConversion.java
public class CaptureConversion {
static <T> void f1(Holder<T> holder) {
T t = holder.get();
System.out.println(t.getClass().getSimpleName());
}
static void f2(Holder<?> holder) {
f1(holder); // Call with captured type
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Holder raw = new Holder<Integer>(1);
// f1(raw); // Produces warnings
f2(raw); // No warnings
Holder rawBasic = new Holder();
rawBasic.set(new Object()); // Warning
f2(rawBasic); // No warnings
// Upcast to Holder<?>, still figures it out:
Holder<?> wildcarded = new Holder<Double>(1.0);
f2(wildcarded);
}
} /* Output:
Integer
Object
Double
*///:~
|
15.11 问题
15.11.1.任何基本类型都不能作为类型参数
15.11.2.实现参数化接口
15.11.3 转型和警告
15.11.4 重载
15.11.5 基类劫持了接口
15.12 自限定的类型
15.12.1 古怪的循环泛型
1
2
3
4
5
6
|
//: generics/CuriouslyRecurringGeneric.java
class GenericType<T> {}
public class CuriouslyRecurringGeneric
extends GenericType<CuriouslyRecurringGeneric> {} ///:~
|
1
2
3
4
5
6
7
8
9
10
|
//: generics/BasicHolder.java
public class BasicHolder<T> {
T element;
void set(T arg) { element = arg; }
T get() { return element; }
void f() {
System.out.println(element.getClass().getSimpleName());
}
} ///:~
|
我们可以在一个古怪的循环泛型中使用BasicHolder
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//: generics/CRGWithBasicHolder.java
class Subtype extends BasicHolder<Subtype> {}
public class CRGWithBasicHolder {
public static void main(String[] args) {
Subtype st1 = new Subtype(), st2 = new Subtype();
st1.set(st2);
Subtype st3 = st1.get();
st1.f();
}
} /* Output:
Subtype
*///:~
|
新类Subtype
接受的参数和返回的值具有Subtype
类型而不仅仅是基类BasicHolder
类型。这就是CRG
的本质:基类用到处类替代其参数。这意味着泛型基类变成了一种其所有到处类的公共功能的模版,但是这些功能对于其所有参数和返回值,将使用导出类型。也就是说,在所产生的类中将使用确切类型而不是基类型。因此,在Subtype
中,传递给set()
的参数和get()
返回的类型都是确切的Subtype
。
15.12.2 自限定
BasicHolder
可以使用任何类型作为其泛型参数,就像下面看到的那样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//: generics/Unconstrained.java
class Other {}
class BasicOther extends BasicHolder<Other> {}
public class Unconstrained {
public static void main(String[] args) {
BasicOther b = new BasicOther(), b2 = new BasicOther();
b.set(new Other());
Other other = b.get();
b.f();
}
} /* Output:
Other
*///:~
|
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
|
//: generics/SelfBounding.java
class SelfBounded<T extends SelfBounded<T>> {
T element;
SelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() { return element; }
}
class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {} // Also OK
class C extends SelfBounded<C> {
C setAndGet(C arg) { set(arg); return get(); }
}
class D {}
// Can't do this:
// class E extends SelfBounded<D> {}
// Compile error: Type parameter D is not within its bound
// Alas, you can do this, so you can't force the idiom:
class F extends SelfBounded {}
public class SelfBounding {
public static void main(String[] args) {
A a = new A();
a.set(new A());
a = a.set(new A()).get();
a = a.get();
C c = new C();
c = c.setAndGet(new C());
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//: generics/NotSelfBounded.java
public class NotSelfBounded<T> {
T element;
NotSelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() { return element; }
}
class A2 extends NotSelfBounded<A2> {}
class B2 extends NotSelfBounded<A2> {}
class C2 extends NotSelfBounded<C2> {
C2 setAndGet(C2 arg) { set(arg); return get(); }
}
class D2 {}
// Now this is OK:
class E2 extends NotSelfBounded<D2> {} ///:~
|
1
2
3
4
5
6
7
8
9
10
|
//: generics/SelfBoundingMethods.java
public class SelfBoundingMethods {
static <T extends SelfBounded<T>> T f(T arg) {
return arg.set(arg).get();
}
public static void main(String[] args) {
A a = f(new A());
}
} ///:~
|
15.12.3 参数协变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//: generics/CovariantReturnTypes.java
class Base {}
class Derived extends Base {}
interface OrdinaryGetter {
Base get();
}
interface DerivedGetter extends OrdinaryGetter {
// Return type of overridden method is allowed to vary:
Derived get();
}
public class CovariantReturnTypes {
void test(DerivedGetter d) {
Derived d2 = d.get();
}
} ///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//: generics/GenericsAndReturnTypes.java
interface GenericGetter<T extends GenericGetter<T>> {
T get();
}
interface Getter extends GenericGetter<Getter> {}
public class GenericsAndReturnTypes {
void test(Getter g) {
Getter result = g.get();
GenericGetter gg = g.get(); // Also the base type
}
} ///:~
|
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
|
//: generics/OrdinaryArguments.java
class OrdinarySetter {
void set(Base base) {
System.out.println("OrdinarySetter.set(Base)");
}
}
class DerivedSetter extends OrdinarySetter {
void set(Derived derived) {
System.out.println("DerivedSetter.set(Derived)");
}
}
public class OrdinaryArguments {
public static void main(String[] args) {
Base base = new Base();
Derived derived = new Derived();
DerivedSetter ds = new DerivedSetter();
ds.set(derived);
ds.set(base); // Compiles: overloaded, not overridden!
}
} /* Output:
DerivedSetter.set(Derived)
OrdinarySetter.set(Base)
*///:~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//: generics/SelfBoundingAndCovariantArguments.java
interface SelfBoundSetter<T extends SelfBoundSetter<T>> {
void set(T arg);
}
interface Setter extends SelfBoundSetter<Setter> {}
public class SelfBoundingAndCovariantArguments {
void testA(Setter s1, Setter s2, SelfBoundSetter sbs) {
s1.set(s2);
// s1.set(sbs); // Error:
// set(Setter) in SelfBoundSetter<Setter>
// cannot be applied to (SelfBoundSetter)
}
} ///:~
|
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
|
//: generics/PlainGenericInheritance.java
class GenericSetter<T> { // Not self-bounded
void set(T arg){
System.out.println("GenericSetter.set(Base)");
}
}
class DerivedGS extends GenericSetter<Base> {
void set(Derived derived){
System.out.println("DerivedGS.set(Derived)");
}
}
public class PlainGenericInheritance {
public static void main(String[] args) {
Base base = new Base();
Derived derived = new Derived();
DerivedGS dgs = new DerivedGS();
dgs.set(derived);
dgs.set(base); // Compiles: overloaded, not overridden!
}
} /* Output:
DerivedGS.set(Derived)
GenericSetter.set(Base)
*///:~
|
15.13 动态类型安全
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
|
//: generics/CheckedList.java
// Using Collection.checkedList().
import typeinfo.pets.*;
import java.util.*;
public class CheckedList {
@SuppressWarnings("unchecked")
static void oldStyleMethod(List probablyDogs) {
probablyDogs.add(new Cat());
}
public static void main(String[] args) {
List<Dog> dogs1 = new ArrayList<Dog>();
oldStyleMethod(dogs1); // Quietly accepts a Cat
List<Dog> dogs2 = Collections.checkedList(
new ArrayList<Dog>(), Dog.class);
try {
oldStyleMethod(dogs2); // Throws an exception
} catch(Exception e) {
System.out.println(e);
}
// Derived types work fine:
List<Pet> pets = Collections.checkedList(
new ArrayList<Pet>(), Pet.class);
pets.add(new Dog());
pets.add(new Cat());
}
} /* Output:
java.lang.ClassCastException: Attempt to insert class typeinfo.pets.Cat element into collection with element type class typeinfo.pets.Dog
*///:~
|
15.14异常
15.15 混型
15.15.1 C++中的混型
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
|
//: generics/Mixins.cpp
#include <string>
#include <ctime>
#include <iostream>
using namespace std;
template<class T> class TimeStamped : public T {
long timeStamp;
public:
TimeStamped() { timeStamp = time(0); }
long getStamp() { return timeStamp; }
};
template<class T> class SerialNumbered : public T {
long serialNumber;
static long counter;
public:
SerialNumbered() { serialNumber = counter++; }
long getSerialNumber() { return serialNumber; }
};
// Define and initialize the static storage:
template<class T> long SerialNumbered<T>::counter = 1;
class Basic {
string value;
public:
void set(string val) { value = val; }
string get() { return value; }
};
int main() {
TimeStamped<SerialNumbered<Basic> > mixin1, mixin2;
mixin1.set("test string 1");
mixin2.set("test string 2");
cout << mixin1.get() << " " << mixin1.getStamp() <<
" " << mixin1.getSerialNumber() << endl;
cout << mixin2.get() << " " << mixin2.getStamp() <<
" " << mixin2.getSerialNumber() << endl;
} /* Output: (Sample)
test string 1 1129840250 1
test string 2 1129840250 2
*///:~
|
15.2 与接口混合
一种更常见的推荐解决方案是使用接口来产生混型效果,就像下面这样:
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
|
//: generics/Mixins.java
import java.util.*;
interface TimeStamped { long getStamp(); }
class TimeStampedImp implements TimeStamped {
private final long timeStamp;
public TimeStampedImp() {
timeStamp = new Date().getTime();
}
public long getStamp() { return timeStamp; }
}
interface SerialNumbered { long getSerialNumber(); }
class SerialNumberedImp implements SerialNumbered {
private static long counter = 1;
private final long serialNumber = counter++;
public long getSerialNumber() { return serialNumber; }
}
interface Basic {
public void set(String val);
public String get();
}
class BasicImp implements Basic {
private String value;
public void set(String val) { value = val; }
public String get() { return value; }
}
class Mixin extends BasicImp implements TimeStamped, SerialNumbered {
private TimeStamped timeStamp = new TimeStampedImp();
private SerialNumbered serialNumber = new SerialNumberedImp();
public long getStamp() { return timeStamp.getStamp(); }
public long getSerialNumber() {
return serialNumber.getSerialNumber();
}
}
public class Mixins {
public static void main(String[] args) {
Mixin mixin1 = new Mixin(), mixin2 = new Mixin();
mixin1.set("test string 1");
mixin2.set("test string 2");
System.out.println(mixin1.get() + " " +
mixin1.getStamp() + " " + mixin1.getSerialNumber());
System.out.println(mixin2.get() + " " +
mixin2.getStamp() + " " + mixin2.getSerialNumber());
}
} /* Output: (Sample)
test string 1 1132437151359 1
test string 2 1132437151359 2
*///:~
|
15.3 使用装饰器模式
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
|
//: generics/decorator/Decoration.java
package generics.decorator;
import java.util.*;
class Basic {
private String value;
public void set(String val) { value = val; }
public String get() { return value; }
}
class Decorator extends Basic {
protected Basic basic;
public Decorator(Basic basic) { this.basic = basic; }
public void set(String val) { basic.set(val); }
public String get() { return basic.get(); }
}
class TimeStamped extends Decorator {
private final long timeStamp;
public TimeStamped(Basic basic) {
super(basic);
timeStamp = new Date().getTime();
}
public long getStamp() { return timeStamp; }
}
class SerialNumbered extends Decorator {
private static long counter = 1;
private final long serialNumber = counter++;
public SerialNumbered(Basic basic) { super(basic); }
public long getSerialNumber() { return serialNumber; }
}
public class Decoration {
public static void main(String[] args) {
TimeStamped t = new TimeStamped(new Basic());
TimeStamped t2 = new TimeStamped(
new SerialNumbered(new Basic()));
//! t2.getSerialNumber(); // Not available
SerialNumbered s = new SerialNumbered(new Basic());
SerialNumbered s2 = new SerialNumbered(
new TimeStamped(new Basic()));
//! s2.getStamp(); // Not available
}
} ///:~
|
15.4 与动态代理混合
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
|
//: generics/DynamicProxyMixin.java
import java.lang.reflect.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Tuple.*;
class MixinProxy implements InvocationHandler {
Map<String,Object> delegatesByMethod;
public MixinProxy(TwoTuple<Object,Class<?>>... pairs) {
delegatesByMethod = new HashMap<String,Object>();
for(TwoTuple<Object,Class<?>> pair : pairs) {
for(Method method : pair.second.getMethods()) {
String methodName = method.getName();
// The first interface in the map
// implements the method.
if (!delegatesByMethod.containsKey(methodName))
delegatesByMethod.put(methodName, pair.first);
}
}
}
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
String methodName = method.getName();
Object delegate = delegatesByMethod.get(methodName);
return method.invoke(delegate, args);
}
@SuppressWarnings("unchecked")
public static Object newInstance(TwoTuple... pairs) {
Class[] interfaces = new Class[pairs.length];
for(int i = 0; i < pairs.length; i++) {
interfaces[i] = (Class)pairs[i].second;
}
ClassLoader cl = pairs[0].first.getClass().getClassLoader();
return Proxy.newProxyInstance(
cl, interfaces, new MixinProxy(pairs));
}
}
public class DynamicProxyMixin {
public static void main(String[] args) {
Object mixin = MixinProxy.newInstance(
tuple(new BasicImp(), Basic.class),
tuple(new TimeStampedImp(), TimeStamped.class),
tuple(new SerialNumberedImp(),SerialNumbered.class));
Basic b = (Basic)mixin;
TimeStamped t = (TimeStamped)mixin;
SerialNumbered s = (SerialNumbered)mixin;
b.set("Hello");
System.out.println(b.get());
System.out.println(t.getStamp());
System.out.println(s.getSerialNumber());
}
} /* Output: (Sample)
Hello
1132519137015
1
*///:~
|
15.16 潜在类型机制
15.17 对缺乏潜在类型机制的补偿
15.18 将函数对象用作策略
15.19 总结:转型真的如此之糟吗?
相关资料