Java基础之十五 泛型
第十五章 泛型
一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义类型。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。
在面对对象编程语言中,多态算是一种泛化机制。
泛型实现了参数化类型的概念,使代码可以应用于多种类型。
15.2 简单泛型
创造容器类促进了泛型的出现。
下面是只能持有单个对象的类:
class Automobile {}
public class Holder1 {
private Automobile a;
public Holder1(Automobile a) { this.a = a; }
Automobile get() { return a; }
}
直接持有Object类型的对象,它可以存储任何类型的对象:
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();
}
}
使用泛型定义:
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一个元组类库
元组它是将一组对象直接打包存储于其中一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。
元组可以具有任意长度,元组中的对象可以是任意不同的类型。
//: 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)
(Amphibian@1f6a7b9, hi, 47)
(Vehicle@35ce36, Amphibian@757aef, hi, 47)
(Vehicle@9cab16, Amphibian@1a46e30, hi, 47, 11.1)
*///:~
15.2.2 一个堆栈类
public class LinkedStack<T> {
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
//: 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 泛型接口
泛型可以应用于接口。例如生成器,这是一种专门负责创建对象的类。这是工厂方法设计模式的一种应用。不过,当使用生成器创建新对象时,它不需要任何参数,而工厂方法一般需要参数。
package coffee;
public class Americano extends Coffee {}
.
.
.
实现Generator< Coffee >接口,随机生成不同类型对象:
package 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);
}
}
使用它生成Fibonacci数列:
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() + " ");
}
}
泛型的一个局限性:基本类型无法作为类型参数,Java SE5具备了自动打包和自动拆包的功能。
通过继承创建适配器:
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 + " ");
}
}
15.4 泛型方法
基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。对于一个static方法而言,无法访问泛型类的类型参数,如果static方法需要使用泛型能力,就必须时期成为泛型方法。
定义泛型方法,只需将泛型参数列表置于返回值之前:
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 杠杆利用类型参数推断
编写一个工具类,它包含各种各样的static方法,专门创建各种容器对象:
public class New
{
public static <K,V> Map<K,V> map(){
return new HashMap<K,V>();
}
}
import 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...
}
} ///:~
类型推断只对赋值操作有效,其他时候并不起作用,如果将一个泛型方法调用的结果作为参数,传递给另一个方法,这是编译器是不会执行类型推断。编译器认为:调用泛型方法后,器返回值被赋给一个Object类型的变量。
显式的类型说明
要显式的指明类型,必须再点操作符与方法名之间插入尖括号,然后把类型置于尖括号内:
import 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 可变参数与泛型方法
泛型方法与可变参数列表能够很好的共存:
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的泛型方法
import 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
public class CountedObject {
private static long counter = 0;
private final long id = counter++;
public long id() { return id; }
public String toString() { return "CountedObject " + id;}
} ///:~
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 简化元组的使用
//: 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)
(Amphibian@7d772e, hi, 47)
(Vehicle@757aef, Amphibian@d9f9c3, hi, 47)
(Vehicle@1a46e30, Amphibian@3e25a5, hi, 47, 11.1)
*///:~
15.4.6 一个Set实用工具
15.5 匿名内部类
泛型还可以应用于内部类以及匿名内部类,使用匿名内部类实现Generator接口:
//: 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元组:
//: 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)
(Vehicle@11b86e7, Amphibian@35ce36, hi, 47)
(Vehicle@757aef, Amphibian@d9f9c3, hi, 47)
*///:~
构建一个商店,它包括走廊,货架和商品:
//: 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 擦除的神秘之处
我们可以声明ArrayList.class,但不能声明ArrayList< Integer >.class
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
*///:~
//: 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]
*///:~
在泛型代码内部,无法获得任何有关泛型参数类型的信息。
Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你为一知道的就是你在使用一个对象。
15.7.1 C++的方式
public class HasF {
public void f() { System.out.println("HasF.f()"); }
} ///:~
//: 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关键字:
class Manipulator2<T extends HasF> {
private T obj;
public Manipulator2(T x) { obj = x; }
public void manipulate() { obj.f(); }
} ///:~
我们说泛型类型参数将擦除到它的第一个边界,我们还提到了类型参数的擦除。编译器实际上会把类型参数替换为它的擦除,就像上面的一样。T擦除到了HasF,就好像在类的声明中用HasF替换了T一样。
只有当你希望使用的类型参数比某个具体类型更加泛化时——也就是说,当你希望代码能够跨多个类工作时。
class ReturnGenericType<T extends HasF> {
private T obj;
public ReturnGenericType(T x) { obj = x; }
public T get() { return obj; }
} ///:~
15.7.2 迁移兼容性
为了减少潜在的关于擦除的混淆,你必须清楚的认识到这不是一个语言特性。它是Java的泛型实现中的一种折中,因为泛型不是Java语言出现时就有的部分。
在基于擦除的实现中,泛型类型被当作第二类型处理,即不能再某些重要的上下文环境中使用的类型。泛型类型只有再静态类型检查期间才出现,再此之后,程序中所有泛型类型都将被擦除,替换为它们的非泛型上界。例如:诸如List< T >这样的类型注解将被擦除为List而普通的类型变量再未指定边界的情况下被擦除未Object。
擦除的核心动机是它使得泛化客户端可以用非泛化得类库来使用。这被称为迁移兼容性。
15.7.3 擦除得问题
擦除主要的正当理由是从非泛型代码到泛型代码的转变过程,以及再不破坏现有类型的情况下,将泛型融入Java语言。
创建实例:
class Foo<T>{
T var;
}
Foo<Cat> f=new Foo<Cat>();
并不是再整个类中的各个地方,类型T都在被替换,编写这个类的代码时,必须知道,它只是一个Object。
//: generics/ErasureAndInheritance.java
class GenericBase<T> {
private T element;
public void set(T arg) { arg = element; }
public T get() { return element; }
}
class Derived1<T> extends GenericBase<T> {}
class Derived2 extends GenericBase {} // No warning
// class Derived3 extends GenericBase<?> {}
// Strange error:
// unexpected type found : ?
// required: class or interface without bounds
public class ErasureAndInheritance {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Derived2 d2 = new Derived2();
Object obj = d2.get();
d2.set(obj); // Warning here!
}
} ///:~
@SuppressWarnings("unchecked")
这个注解被放置再可以产生这类警告的方法智商,而不是整个类上。当你要关闭警告时,最好时尽量聚焦,这样就不会因为过于宽泛的关闭警告,而导致意外遮蔽掉真正的问题。
15.7.4 边界处的动作
正是因为有了擦除,泛型最让人困惑的地方就是可以表示没有任何意义的事物:
//: 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]
*///:~
对于创建数组,使用Array.newInstance()是推荐方式。
import java.util.*;
public class ListMaker<T> {
List<T> create() { //这其中的<T>被移除了,在运行时,这个内部没有任何<T>
return new ArrayList<T>();
}
public static void main(String[] args) {
ListMaker<String> stringMaker= new ListMaker<String>();
List<String> stringList = stringMaker.create();
}
} ///:~
在返回list之前,将某些对象放入其中:
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 >。即使擦除在方法或内部移除了有关实际类型的信息,编译器仍旧可以确保在方法或类中使用的类型的内部一致性。
擦除在方法体中移除了类型信息,所有在运行时的问题就边界:即对象进入和离开方法的地点。
泛型中的所有动作都发生在边界处——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。边界就是发生动作的地方
15.8 擦出的补偿
擦除丢失了在泛型代码中执行某些操作的能力。任何运行时需要知道确切类型信息的操作都将无法工作:
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 new T无法实现,部分原因时擦出,另一部分是因为编译器不能验证T具有默认构造器
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[100]; // Unchecked warning
}
} ///:~
如果引入类型标签,就可以转而使用动态的isInstance():
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 创建类型实例
传递一个工厂对象,并使用它来创建新的实例:
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);//将会失败,因为Integer没有默认的构造参数
} catch(Exception e) {
print("ClassAsFactory<Integer> failed");
}
}
} /* Output:
ClassAsFactory<Employee> succeeded
ClassAsFactory<Integer> failed
*///:~
使用显示的工厂,并限制类型:
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());
}
} ///:~
模板方法设计模式:
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 泛型数组
不能创建泛型数组。一般的解决办法是再任何想要创建泛型数组的地方都使用ArrayList:
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); }
} ///:~
可以按照编译器喜欢方法定义一个引用:
class Generic<T> {}
public class ArrayOfGenericReference {
static Generic<Integer>[] gia;
} ///:~
编译器永远都不能创建这个确切的数组。
创建一个Object数组,将其转型为所希望的数组类型。这是可以编译的,但是不能运用。
public class ArrayOfGeneric {
static final int SIZE = 100;
static Generic<Integer>[] gia;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// 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[]
*///:~
成功创建泛型数组唯一方式就是创建一个被擦除类型的新数组,然后对其转型:
public class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray(int sz) {
array = (T[])new Object[sz];
}//创建Object转型为T
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();
}
} ///:~
不能声明T[] array=new T[sz],因此我们创建了一个对象数组,然后将其转型。
因为有了擦除,数组的运行时类型就只能是Object[]。如果我们立即将其转型为T[]那么再编译器该数组的实际类型就将丢失,而编译器可能会错过某些潜在的错误检查。因为这样,最好再集合内部使用Object[],然后当你使用数组元素时,添加一个对T的转型。
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;
*///:~
在内部将array当作Object[]而不是T[]处理的优势:我们不太可能忘记这个数组的运行时类型,从而意外地引入缺陷。
对于新代码,应该传递一个类型标识:
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:
for(int i=0;i<10;i++)
gai.put(i,i);
Integer[] ia = gai.rep();
for (int i:ia )
System.out.println(i);
}
} ///:~
类型标记Class< T >被传递到构造器中,以便从擦除中恢复,使得我们可以创建需要得实际类型得数组。
15.9 边界
边界使得你可以在用于泛型得参数类型上设置限制条件。尽管这使得你可以强制规定泛型可以应用得类型,但是其潜在得一个更重要得效果是你可以按照自己的边界类型来调用方法。
因为擦除移除了类型信息,所以,可以i用无界泛型参数调用的方法只是那些可以用Object调用的方法。如果能够将这个参数限制为某个类型子集,那么你就可以用这些类型子集来调用方法。为了执行这种限制,Java泛型重用了extends关键字。
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();
}
} ///:~
通过继承将边界传递下去:
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();
}
} ///:~
更多层次的继承:
// 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>//POWER边界来源于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 通配符
可以向导出类型的数组赋予基类型的数组引用:
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[2] = new Fruit(); // ArrayStoreException 无法向上转型
} catch(Exception e) { System.out.println(e); }
try {
// Compiler allows you to add Oranges:
fruit[3] = new Orange(); // ArrayStoreException
} catch(Exception e) { System.out.println(e); }
}
} /* Output:
java.lang.ArrayStoreException: Fruit
java.lang.ArrayStoreException: Orange
*///:~
数组对象可以保留有关它们包含的对象类型的规则。
import java.util.*;
public class NonCovariantGenerics {
// Compile Error: incompatible types:
List<Fruit> flist = new ArrayList<Apple>();//不能把一个Apple容器赋值给一个Fruit容器
} ///:~
于数组不同,泛型没有内建的协变类型。有时你想要在两个类型之间建立某种类型的向上转型关系,这正是通配符所允许的:
import java.util.*;
public class GenericsAndCovariance {
public static void main(String[] args) {
// Wildcards allow covariance:
List<? extends Fruit> flist = new ArrayList<Apple>();//这里将向上转型将丢失向里传递任何对象的能力,以至于无法传入任何类型,Object也不可以传入。
// 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 编译器有多聪明
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()); // 对象参数是Object
flist.indexOf(new Apple()); // Argument is 'Object'
//flist.add(new Apple());这里Add需要的参数是? extends Fruit,但是它并不知道需要那个具体的类型,所有它不会接受任何参数
}
} ///:~
add()将接受一个具有泛型参数类型的参数,但是contains()和indexOf()将接受Object类型的参数,因此当你指定一个ArrayList<? extends Fruit>时,add()的参数就变成了? extends Fruit。
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 逆变
超类型通配符,可以声明通配符是由某个特定类的任何基类来界定的,方法是指定<? super MyClass>,甚至使用类型参数<? super T>。
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
}
} ///:~
超类型边界放松了在可以向方法传递的参数上所做的限制:
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(); }
} ///:~
15.10.3 无界通配符
无界通配符< ? >看起来意味着任何事物,因此使用无界通配符好像等价于使用原生类型。
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);
}
} ///:~
< ? >可以被认为是一种装饰,实际上,它是在声明:我是想用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>());
}
} ///:~
事实上,由于泛型参数将擦出到它的第一个边界,因此LIst看起来等价于LIst< Object >,而List实际上也是List< Object >——除非这些语句都不为真。List实际上表示持有任何Object类型的原生List,而List表示具有某种特定类型的非原生List。
//: generics/Wildcards.java
// Exploring the meaning of wildcards.
public class Wildcards {
// Raw argument:
static void rawArgs(Holder holder, Object arg) {
holder.set(arg); // Warning:编译器知道Holder是泛型,因此向set传递Object是不安全的
// 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:原生类型将持有任何类型的组合,但Holder<?>将持有具有某种具体类型的同构集合,因此,不能向其中传递Object。
// 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 t = holder.get(); // Error:
// Incompatible types: found Object, required T
// OK, but type information has been lost:
Object obj = holder.get();//这里传出的类型可以是任何超类型,所以这里使用Object才是安全的。
}
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)
}
} ///:~
使用确切类型来替代通配符类型的好处是,可以用泛型参数来做更多的事,但是使用通配符使得你必须接收范围更宽的参数化类型作为参数。
15.10.4 捕获转换
有一个情况特定需要使用而不是原生类型。如果向一个使用方法传递原生类型,那么对编译器来说,可能会推断出实际类型,使得这个方法可以回转并调用另一个使用这个确切类型的方法,它被称为:捕获转换。因为未指定的通配符类型被捕获,并被转换为确切类型。
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.这里参数类型在调用f2的过程中被捕获,因此它可以在对f1的调用中被使用。
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 任何基本类型都不能作为类型参数
使用基本类型的包装器:
import java.util.*;
public class ListOfInt {
public static void main(String[] args) {
List<Integer> li = new ArrayList<Integer>();
for(int i = 0; i < 5; i++)
li.add(i);
for(int i : li)
System.out.print(i + " ");
}
} /* Output:
0 1 2 3 4
*///:~
如果对性能有要求,可以使用专门的适配基本类型的容器版本:Org.apache.commons.collections.primitives。
使用另一种方法创建Byte的Set:
import java.util.*;
public class ByteSet {
Byte[] possibles = { 1,2,3,4,5,6,7,8,9 };
Set<Byte> mySet =
new HashSet<Byte>(Arrays.asList(possibles));
// But you can't do this:
// Set<Byte> mySet2 = new HashSet<Byte>(
// Arrays.<Byte>asList(1,2,3,4,5,6,7,8,9));
} ///:~
使用泛型Generator接口:
import net.mindview.util.*;
// Fill an array using a generator:
class FArray {
public static <T> T[] fill(T[] a, Generator<T> gen) {
for(int i = 0; i < a.length; i++)
a[i] = gen.next();
return a;
}
}
public class PrimitiveGenericTest {
public static void main(String[] args) {
String[] strings = FArray.fill(
new String[8], new RandomGenerator.String());
for(String s : strings)
System.out.println(s);
Integer[] integers = FArray.fill(
new Integer[7], new RandomGenerator.Integer());
for(int i: integers)
System.out.println(i);
// Autoboxing won't save you here. This won't compile:
// int[] b =
// FArray.fill(new int[7], new RandIntGenerator());
}
} /* Output:
YNzbrnyGcF
OWZnTcQrGs
eGZMmJMRoE
suEcUOneOE
dLsmwHLGEa
hKcxrEqUCB
bkInaMesbt
7052
6665
2654
3909
5202
2209
5458
*///:~
15.11.2 实现参数化接口
一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。
interface Payable<T> {}
class Employee1 implements Payable<Employee1> {}
class Hourly extends Employee implements Payable<Hourly> {} ///:~这里都被擦除为Object
public class MultipleInterfaceVariants{
public static void main(String[] args){
}
}
15.11.3 转型和警告
使用带有泛型类型参数的转型或instanceof不会有任何效果。
下面的容器在内部将各个值存储为Object,并在获取这些值的时候,再将它们转型回T:
class FixedSizeStack<T> {
private int index = 0;
private Object[] storage;
public FixedSizeStack(int size) {
storage = new Object[size];
}
public void push(T item) { storage[index++] = item; }
@SuppressWarnings("unchecked")
public T pop() { return (T)storage[--index]; }//由于擦除的原因,编译器无法知道这个转型是否安全的,并且pop()方法实际上并没有执行任何转型。
}
public class GenericCast {
public static final int SIZE = 10;
public static void main(String[] args) {
FixedSizeStack<String> strings =
new FixedSizeStack<String>(SIZE);
for(String s : "A B C D E F G H I J".split(" "))
strings.push(s);
for(int i = 0; i < SIZE; i++) {
String s = strings.pop();
System.out.print(s + " ");
}
}
} /* Output:
J I H G F E D C B A
*///:~
泛型没有消除对转型的需要,这就会由编译器产生警告,而这个警告是不恰当的:
import java.io.*;
import java.util.*;
public class NeedCasting {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream(""));
List<Widget> shapes = (List<Widget>)in.readObject();//readObject无法知道它正在读取的是什么,因此它返回的是必须转型的对象
}
} ///:~
泛型类来转型:
import java.io.*;
import java.util.*;
public class ClassCasting {
@SuppressWarnings("unchecked")
public void f(String[] args) throws Exception {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream(args[0]));
// Won't Compile:
// List<Widget> lw1 =
// List<Widget>.class.cast(in.readObject());
List<Widget> lw2 = List.class.cast(in.readObject());
}
} ///:~
15.11.4 重载
下面的程序不能编译:
import java.util.*;
public class UseList<W,T> {
void f1(List<T> v) {}//重载将产生相同的类型签名
void f1(List<W> v) {}
} ///:~
15.11.5 基类劫持了接口
创建一个于其他对象比较类:
public class ComparablePet
implements Comparable<ComparablePet> {
public int compareTo(ComparablePet arg) { return 0; }
} ///:~
对其子类进行窄化没有意义:代码是不能运行的
class Cat extends ComparablePet implements Comparable<Cat>{
// Error: Comparable cannot be inherited with
// different arguments: <Cat> and <Pet>
public int compareTo(Cat arg) { return 0; }
} ///:~
下面代码确是可以运行的,只要它们精确的类相同:
class Hamster extends ComparablePet
implements Comparable<ComparablePet> {
public int compareTo(ComparablePet arg) { return 0; }
}
// Or just:
class Gecko extends ComparablePet {
public int compareTo(ComparablePet arg) { return 0; }
} ///:~
15.12 自限定的类型
在Java泛型中,有一个好像是经常性出现的惯用法:
class SelfBounded<T extends SelfBounded<T>>
这就像两面镜子彼此照向对方引起目眩效果,是一种无限反射。SelfBounded类接受泛型参数T,而T由一个边界类限定,这个边界就是拥有T作为参数的SelfBounded。
15.12.1 古怪的循环泛型
不能直接继承一个泛型参数,但是,可以继承在其自己的定义中使用这个泛型参数的类。
class GenericType<T> {}
public class CuriouslyRecurringGeneric
extends GenericType<CuriouslyRecurringGeneric> {} ///:~
这被称为古怪的循环泛型。
Java中的泛型关乎参数和返回类型,因此它能够产生使用导出类作为其参数和返回类型的基类。它还能将导出类型用作其域类型,甚至那些将被擦除为Object的类型。
public class BasicHolder<T> {
T element;
void set(T arg) { element = arg; }
T get() { return element; }
void f() {
System.out.println(element.getClass().getSimpleName());
}
} ///:~
这是一个普通的泛型类型,下面再一个古怪的循环泛型中使用它:
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
*///:~
CRG本质:基类用导出类替代其参数
在所产生的类中将使用确切类型而不是基类型。
15.12.2 自限定
BasicHolder可以使用任何类型作为其泛型参数:
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
*///:~
自限定将采取额外的步骤,强制泛型当作其自己的边界参数来使用:
class SelfBounded<T extends SelfBounded<T>> {
T element;
SelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() {System.out.println(this.getClass().getSimpleName()); 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());
}
} ///:~
自限定参数意义:它可以保证类型参数必须与正在被定义的类相同。
可以移除自限定这个限制,这样所有的类仍旧是可以编译的。
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> {} ///:~
很明显,自限定限制只能强制作用于继承关系,如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基类型。
还可以将自限定用于泛型方法:
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 参数协变
自限定类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化。
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();
}
} ///:~
自限定泛型事实上将产生确切的导出类型作为其返回值:
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
}
} ///:~
在使用自限定类型时,在导出类只有一个方法,并且这个方法接收导出类型而不是基类型为参数:
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()的尝试,因为没有任何方法具有这样的签名。实际上,这个参数已经被覆盖了
// set(Setter) in SelfBoundSetter<Setter>
// cannot be applied to (SelfBoundSetter)
}
} ///:~
如果不使用自限定类型,普通的继承机制就会介入,而将能够重载:
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 动态类型安全
Java SE5的java.util.Collections中有一组便利工具,它们是:静态方法checkedCollection(),checkedList(),checkedMap(),checkedSet(),checkedSortedMap()和checkedSortedSet()。这些方法每一个都会将你希望动态检查的容器作为一个参数接受,并将你希望强制要求的类型作为第二个参数接受。
import 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 异常
由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为编译期和运行时都必须知道异常的确切类型,泛型类也不能直接或间接继承自Throwable。
import java.util.*;
interface Processor<T,E extends Exception> {
void process(List<T> resultCollector) throws E;
}
class ProcessRunner<T,E extends Exception>
extends ArrayList<Processor<T,E>> {
List<T> processAll() throws E {
List<T> resultCollector = new ArrayList<T>();
for(Processor<T,E> processor : this)
processor.process(resultCollector);
return resultCollector;
}
}
class Failure1 extends Exception {}
class Processor1 implements Processor<String,Failure1> {
static int count = 3;
public void
process(List<String> resultCollector) throws Failure1 {
if(count-- > 1)
resultCollector.add("Hep!");
else
resultCollector.add("Ho!");
if(count < 0)
throw new Failure1();
}
}
class Failure2 extends Exception {}
class Processor2 implements Processor<Integer,Failure2> {
static int count = 2;
public void
process(List<Integer> resultCollector) throws Failure2 {
if(count-- == 0)
resultCollector.add(47);
else {
resultCollector.add(11);
}
if(count < 0)
throw new Failure2();
}
}
public class ThrowGenericException {
public static void main(String[] args) {
ProcessRunner<String,Failure1> runner =
new ProcessRunner<String,Failure1>();
for(int i = 0; i < 3; i++)
runner.add(new Processor1());
try {
System.out.println(runner.processAll());
} catch(Failure1 e) {
System.out.println(e);
}
ProcessRunner<Integer,Failure2> runner2 =
new ProcessRunner<Integer,Failure2>();
for(int i = 0; i < 3; i++)
runner2.add(new Processor2());
try {
System.out.println(runner2.processAll());
} catch(Failure2 e) {
System.out.println(e);
}
}
} ///:~
15.15 混型
基本概念:混合多个类的能力,以产生一个可以表示混型中所有类型的类。
混型的价值之一是它们可以将特性和行为一致地应用于多个类之上。如果想在混型中修改某些东西,作为一中意外的好处,这些修改将会应用于混型所应用的所有类型之上。
15.15.2 与接口混合
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 {//使用代理,每个混入类型都要求在Mixin中有一个相应的域,必须在Mixin中编写必须的方法,将方法调用转发给恰当的对象
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.15.3 使用装饰器模式
观察混型的使用方式时,就会发现混型概念好像与装饰器设计模式很像。装饰器经常用于满足各种可能的组合,而直接子类化会产生过多的类,因此不实际。
装饰器模式使用分层对象来动态透明地想单个对象中添加责任。装饰器指定包装在最初的对象周围的所有对象都具有相同的基本接口。某些事物是可装饰的,可以通过将其他类包装在这个可装饰对象的周围,来将功能分层。
装饰器是通过使用组合和形式化结构来实现的,而混型是基于继承的。
将前面的示例改写为使用装饰器:
package 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.15.4 与动态代理混合
可以使用动态代理来创建一种比装饰器更贴近混型的模型机制。通过使用动态代理,所产生的类的动态类型将会是已经混入的组合类型。
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 潜在类型机制
当要在泛型类型上执行操作时,就会产生问题,因为擦除要求指定可能会用到泛型类型的边界,以安全的调用代码中的泛型对象上具体方法。这是对泛化的一种明显限制,因为必须限制你的泛型类型,使它们继承自特定的类,或者实现特定接口。某些情况下,你最终可能会使用普通类或普通接口,因为限定边界的泛型可能会和指定类或接口没有任何区别。
某些变成语言提供的一种解决方案称为潜在类型机制或结构化类型机制。
潜在类型机制使得你可以横跨类继承结构,调用不属于某个公共接口的方法。因此,实际上一段代码可以声明:我不关心你是什么类型,只要你可以speak()和sit()即可呃。由于不要求具体类型,因此代码可以更加泛化。
潜在机制一种代码组织和复用机制
潜在机制不要求静态或动态类型检查
15.17 对缺乏潜在类型机制的补偿
尽管Java不支持潜在类型机制,但是这并不意味着有界泛型代码不能再不同的类型层次结构之间应用。
15.17.1 反射
通过反射实现潜在类型机制:
//: generics/LatentReflection.java
// Using Reflection to produce latent typing.
import java.lang.reflect.*;
import static net.mindview.util.Print.*;
// Does not implement Performs:
class Mime {
public void walkAgainstTheWind() {}
public void sit() { print("Pretending to sit"); }
public void pushInvisibleWalls() {}
public String toString() { return "Mime"; }
}
// Does not implement Performs:
class SmartDog {
public void speak() { print("Woof!"); }
public void sit() { print("Sitting"); }
public void reproduce() {}
}
class CommunicateReflectively {
public static void perform(Object speaker) {
Class<?> spkr = speaker.getClass();
try {
try {
Method speak = spkr.getMethod("speak");
speak.invoke(speaker);
} catch(NoSuchMethodException e) {
print(speaker + " cannot speak");
}
try {
Method sit = spkr.getMethod("sit");
sit.invoke(speaker);
} catch(NoSuchMethodException e) {
print(speaker + " cannot sit");
}
} catch(Exception e) {
throw new RuntimeException(speaker.toString(), e);
}
}
}
public class LatentReflection {
public static void main(String[] args) {
CommunicateReflectively.perform(new SmartDog());
CommunicateReflectively.perform(new Robot());
CommunicateReflectively.perform(new Mime());
}
} /* Output:
Woof!
Sitting
Click!
Clank!
Mime cannot speak
Pretending to sit
*///:~
这些类完全是彼此分离的,没有任何公共基类或接口。通过反射动态确定所需要的方法是否可用并调用它们。
15.17.2 将一个方法应用于序列
使用可变参数来解决:
import java.lang.reflect.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class Apply {
public static <T, S extends Iterable<? extends T>>
void apply(S seq, Method f, Object... args) {
try {
for(T t: seq)
f.invoke(t, args);
} catch(Exception e) {
// Failures are programmer errors
throw new RuntimeException(e);
}
}
}
class Shape {
public void rotate() { print(this + " rotate"); }
public void resize(int newSize) {
print(this + " resize " + newSize);
}
}
class Square extends Shape {}
class FilledList<T> extends ArrayList<T> {
public FilledList(Class<? extends T> type, int size) {
try {
for(int i = 0; i < size; i++)
// Assumes default constructor:
add(type.newInstance());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
class ApplyTest {
public static void main(String[] args) throws Exception {
List<Shape> shapes = new ArrayList<Shape>();
for(int i = 0; i < 10; i++)
shapes.add(new Shape());
Apply.apply(shapes, Shape.class.getMethod("rotate"));
Apply.apply(shapes,
Shape.class.getMethod("resize", int.class), 5);
List<Square> squares = new ArrayList<Square>();
for(int i = 0; i < 10; i++)
squares.add(new Square());
Apply.apply(squares, Shape.class.getMethod("rotate"));
Apply.apply(squares,
Shape.class.getMethod("resize", int.class), 5);
Apply.apply(new FilledList<Shape>(Shape.class, 10),
Shape.class.getMethod("rotate"));
Apply.apply(new FilledList<Shape>(Square.class, 10),
Shape.class.getMethod("rotate"));
SimpleQueue<Shape> shapeQ = new SimpleQueue<Shape>();
for(int i = 0; i < 5; i++) {
shapeQ.add(new Shape());
shapeQ.add(new Square());
}
Apply.apply(shapeQ, Shape.class.getMethod("rotate"));
}
} /* (Execute to see output) *///:~
apple(0方法可接受任何实现了Iterable接口的事物,包括List这样的所有Collection类。
比如下面这个类:
import java.util.*;
public class SimpleQueue<T> implements Iterable<T> {
private LinkedList<T> storage = new LinkedList<T>();
public void add(T t) { storage.offer(t); }
public T get() { return storage.poll(); }
public Iterator<T> iterator() {
return storage.iterator();
}
} ///:~
15.17.3 当你并未拥有正确的接口时
因为Iterable接口是已经内建的,而它正是我们需要的,如果不存在刚好适合的接口呢?
import java.util.*;
// Doesn't work with "anything that has an add()." There is
// no "Addable" interface so we are narrowed to using a
// Collection. We cannot generalize using generics in
// this case.
public class Fill {
public static <T> void fill(Collection<T> collection,
Class<? extends T> classToken, int size) {
for(int i = 0; i < size; i++)
// Assumes default constructor:
try {
collection.add(classToken.newInstance());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
class Contract {
private static long counter = 0;
private final long id = counter++;
public String toString() {
return getClass().getName() + " " + id;
}
}
class TitleTransfer extends Contract {}
class FillTest {
public static void main(String[] args) {
List<Contract> contracts = new ArrayList<Contract>();
Fill.fill(contracts, Contract.class, 3);
Fill.fill(contracts, TitleTransfer.class, 2);
for(Contract c: contracts)
System.out.println(c);
SimpleQueue<Contract> contractQueue =
new SimpleQueue<Contract>();
// Won't work. fill() is not generic enough:
// Fill.fill(contractQueue, Contract.class, 3);
}
} /* Output:
Contract 0
Contract 1
Contract 2
TitleTransfer 3
TitleTransfer 4
*///:~
15.17.4 用适配器仿真潜在类型机制
潜在类型机制意味着你可以编写代码声明:我不关心我再这里使用的类型,只要它具有这些方法即可。实际上,潜在类型机制创建了一个包含所需要方法的隐试接口。因此它遵循这样的规则:如果我们手工编写了必须的接口,那么它就应该能够解决问题。
我们可以使用设配器来适配已有的接口,产生想要的接口:
import coffee.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
interface Addable<T> { void add(T t); }
public class Fill2 {
// Classtoken version:
public static <T> void fill(Addable<T> addable,
Class<? extends T> classToken, int size) {
for(int i = 0; i < size; i++)
try {
addable.add(classToken.newInstance());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
// Generator version:
public static <T> void fill(Addable<T> addable,
Generator<T> generator, int size) {
for(int i = 0; i < size; i++)
addable.add(generator.next());
}
}
// To adapt a base type, you must use composition.
// Make any Collection Addable using composition:
class AddableCollectionAdapter<T> implements Addable<T> {
private Collection<T> c;
public AddableCollectionAdapter(Collection<T> c) {
this.c = c;
}
public void add(T item) { c.add(item); }
}
// A Helper to capture the type automatically:
class Adapter {
public static <T>
Addable<T> collectionAdapter(Collection<T> c) {
return new AddableCollectionAdapter<T>(c);
}
}
// To adapt a specific type, you can use inheritance.
// Make a SimpleQueue Addable using inheritance:
class AddableSimpleQueue<T>
extends SimpleQueue<T> implements Addable<T> {
public void add(T item) { super.add(item); }
}
class Fill2Test {
public static void main(String[] args) {
// Adapt a Collection:
List<Coffee> carrier = new ArrayList<Coffee>();
Fill2.fill(
new AddableCollectionAdapter<Coffee>(carrier),
Coffee.class, 3);
// Helper method captures the type:
Fill2.fill(Adapter.collectionAdapter(carrier),
Latte.class, 2);
for(Coffee c: carrier)
print(c);
print("----------------------");
// Use an adapted class:
AddableSimpleQueue<Coffee> coffeeQueue =
new AddableSimpleQueue<Coffee>();
Fill2.fill(coffeeQueue, Mocha.class, 4);
Fill2.fill(coffeeQueue, Latte.class, 1);
for(Coffee c: coffeeQueue)
print(c);
}
} /* Output:
Coffee 0
Coffee 1
Coffee 2
Latte 3
Latte 4
----------------------
Mocha 5
Mocha 6
Mocha 7
Mocha 8
Latte 9
*///:~
15.18 将函数对象用作策略
策略设计模式,这种设计模式可以产生更优雅代码,因为它将变化的事物完全隔离到了一个函数对象中。
函数对象的价值就在于,于普通方法不同,它们可以传递出去,并且还可以拥有在多个调用之间持久化的状态。
函数对象主要是由其目的来区别的。这里的目的就是要创建某种事物,使它的行为就像是一个可以传递出去的单个方法一样。
//: generics/Functional.java
import java.math.*;
import java.util.concurrent.atomic.*;
import java.util.*;
import static net.mindview.util.Print.*;
// Different types of function objects:
interface Combiner<T> { T combine(T x, T y); }
interface UnaryFunction<R,T> { R function(T x); }
interface Collector<T> extends UnaryFunction<T,T> {
T result(); // Extract result of collecting parameter
}
interface UnaryPredicate<T> { boolean test(T x); }
public class Functional {
// Calls the Combiner object on each element to combine
// it with a running result, which is finally returned:
public static <T> T
reduce(Iterable<T> seq, Combiner<T> combiner) {
Iterator<T> it = seq.iterator();
if(it.hasNext()) {
T result = it.next();
while(it.hasNext())
result = combiner.combine(result, it.next());
return result;
}
// If seq is the empty list:
return null; // Or throw exception
}
// Take a function object and call it on each object in
// the list, ignoring the return value. The function
// object may act as a collecting parameter, so it is
// returned at the end.
public static <T> Collector<T>
forEach(Iterable<T> seq, Collector<T> func) {
for(T t : seq)
func.function(t);
return func;
}
// Creates a list of results by calling a
// function object for each object in the list:
public static <R,T> List<R>
transform(Iterable<T> seq, UnaryFunction<R,T> func) {
List<R> result = new ArrayList<R>();
for(T t : seq)
result.add(func.function(t));
return result;
}
// Applies a unary predicate to each item in a sequence,
// and returns a list of items that produced "true":
public static <T> List<T>
filter(Iterable<T> seq, UnaryPredicate<T> pred) {
List<T> result = new ArrayList<T>();
for(T t : seq)
if(pred.test(t))
result.add(t);
return result;
}
// To use the above generic methods, we need to create
// function objects to adapt to our particular needs:
static class IntegerAdder implements Combiner<Integer> {
public Integer combine(Integer x, Integer y) {
return x + y;
}
}
static class
IntegerSubtracter implements Combiner<Integer> {
public Integer combine(Integer x, Integer y) {
return x - y;
}
}
static class
BigDecimalAdder implements Combiner<BigDecimal> {
public BigDecimal combine(BigDecimal x, BigDecimal y) {
return x.add(y);
}
}
static class
BigIntegerAdder implements Combiner<BigInteger> {
public BigInteger combine(BigInteger x, BigInteger y) {
return x.add(y);
}
}
static class
AtomicLongAdder implements Combiner<AtomicLong> {
public AtomicLong combine(AtomicLong x, AtomicLong y) {
// Not clear whether this is meaningful:
return new AtomicLong(x.addAndGet(y.get()));
}
}
// We can even make a UnaryFunction with an "ulp"
// (Units in the last place):
static class BigDecimalUlp
implements UnaryFunction<BigDecimal,BigDecimal> {
public BigDecimal function(BigDecimal x) {
return x.ulp();
}
}
static class GreaterThan<T extends Comparable<T>>
implements UnaryPredicate<T> {
private T bound;
public GreaterThan(T bound) { this.bound = bound; }
public boolean test(T x) {
return x.compareTo(bound) > 0;
}
}
static class MultiplyingIntegerCollector
implements Collector<Integer> {
private Integer val = 1;
public Integer function(Integer x) {
val *= x;
return val;
}
public Integer result() { return val; }
}
public static void main(String[] args) {
// Generics, varargs & boxing working together:
List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Integer result = reduce(li, new IntegerAdder());
print(result);
result = reduce(li, new IntegerSubtracter());
print(result);
print(filter(li, new GreaterThan<Integer>(4)));
print(forEach(li,
new MultiplyingIntegerCollector()).result());
print(forEach(filter(li, new GreaterThan<Integer>(4)),
new MultiplyingIntegerCollector()).result());
MathContext mc = new MathContext(7);
List<BigDecimal> lbd = Arrays.asList(
new BigDecimal(1.1, mc), new BigDecimal(2.2, mc),
new BigDecimal(3.3, mc), new BigDecimal(4.4, mc));
BigDecimal rbd = reduce(lbd, new BigDecimalAdder());
print(rbd);
print(filter(lbd,
new GreaterThan<BigDecimal>(new BigDecimal(3))));
// Use the prime-generation facility of BigInteger:
List<BigInteger> lbi = new ArrayList<BigInteger>();
BigInteger bi = BigInteger.valueOf(11);
for(int i = 0; i < 11; i++) {
lbi.add(bi);
bi = bi.nextProbablePrime();
}
print(lbi);
BigInteger rbi = reduce(lbi, new BigIntegerAdder());
print(rbi);
// The sum of this list of primes is also prime:
print(rbi.isProbablePrime(5));
List<AtomicLong> lal = Arrays.asList(
new AtomicLong(11), new AtomicLong(47),
new AtomicLong(74), new AtomicLong(133));
AtomicLong ral = reduce(lal, new AtomicLongAdder());
print(ral);
print(transform(lbd,new BigDecimalUlp()));
}
} /* Output:
28
-26
[5, 6, 7]
5040
210
11.000000
[3.300000, 4.400000]
[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
311
true
265
[0.000001, 0.000001, 0.000001, 0.000001]
*///:~