- 泛型還可以應用於內部類以及匿名內部類。
public class Customer {
private static long counter=1;
private final long id=counter++;
public Customer() {
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
'}';
}
public static Generator<Customer> generator(){
return new Generator<Customer>() {
@Override
public Customer next() {
return new Customer();
}
};
}
}
public class Teller {
private static long customer = 1;
private final long id = customer++;
public Teller() {
}
@Override
public String toString() {
return "Teller{" +
"id=" + id +
'}';
}
public static Generator<Teller> tellerGenerator(){
return new Generator<Teller>() {
@Override
public Teller next() {
return new Teller();
}
};
}
}
public class Generators {
static <T> Collection<T> fill(Collection<T> collection,
Generator<T> generator,
int n) {
for (int i = 0; i < n; i++)
collection.add(generator.next());
return collection;
}
}
public class BankTeller {
static void serve(Teller teller,Customer customer){
System.out.println("Teller: "+teller+" Customer: "+customer);
}
public static void main(String[] args) {
Random random=new Random(47);
Queue<Customer> line=new LinkedList<>();
//向集合中填充數據
Generators.fill(line,Customer.generator(),15);
List<Teller> tellerList=new ArrayList<>();
//同上
Generators.fill(tellerList,Teller.tellerGenerator(),4);
//循環遍歷集合
line.stream().forEach(each->serve(tellerList.get(random.nextInt(tellerList.size())),each));
}
}
//運行結果爲
Teller: Teller{id=3} Customer: Customer{id=1}
Teller: Teller{id=2} Customer: Customer{id=2}
Teller: Teller{id=3} Customer: Customer{id=3}
Teller: Teller{id=1} Customer: Customer{id=4}
Teller: Teller{id=1} Customer: Customer{id=5}
Teller: Teller{id=3} Customer: Customer{id=6}
Teller: Teller{id=1} Customer: Customer{id=7}
Teller: Teller{id=2} Customer: Customer{id=8}
Teller: Teller{id=3} Customer: Customer{id=9}
Teller: Teller{id=3} Customer: Customer{id=10}
Teller: Teller{id=2} Customer: Customer{id=11}
Teller: Teller{id=4} Customer: Customer{id=12}
Teller: Teller{id=2} Customer: Customer{id=13}
Teller: Teller{id=1} Customer: Customer{id=14}
Teller: Teller{id=1} Customer: Customer{id=15}
- Customer 和 Teller 類都只有 private 的構造器,這可以強制你必須是用 Generator 對象。 Customer 有一個 generator() 方法,每次執行它都會生成一個新的 Generator<Customer> 對象。
- 我們其實不需要多個 Generator 對象,Teller 就只創建了一個 public 的generator 對象。在 main() 方法中可以看到,這兩種創建 Generator的方式都在 fill() 中用到了。
- 由於Customer 中的 generator() 方法,以及Teller 中的 Generator 對象都聲明成了 static 的,所以它們無法作爲接口的一部分,因此無法用接口這種特定的慣用法來泛化這二者。儘管如此,它們在 fill() 方法中都工作得很好。
構建複雜模型
- 泛型的一個重要好處就是能夠簡單而安全地創建複雜的模型。
//例如,我們可以很容易地創建List 元組 如下
public class TwoTuple<A,B> {
public final A a;
public final B b;
public TwoTuple(A a, B b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return "TwoTuple{" +
"a=" + a +
", b=" + b +
'}';
}
}
public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {
public final C c;
public ThreeTuple(A a, B b, C c) {
super(a, b);
this.c = c;
}
@Override
public String toString() {
return "ThreeTuple{" +
"c=" + c +
", a=" + a +
", b=" + b +
'}';
}
}
class FourTuple<A, B, C, D> extends ThreeTuple<A, B, C> {
public final D d;
public FourTuple(A a, B b, C c, D d) {
super(a, b, c);
this.d = d;
}
@Override
public String toString() {
return "FourTuple{" +
"c=" + c +
", d=" + d +
", a=" + a +
", b=" + b +
'}';
}
}
class Amphibian {}
class Vehicle {}
class TupleTest {
static TwoTuple<String, Integer> f() {
return new TwoTuple<>("hi", 47);
}
}
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> tupleList=new TupleList<>();
//向集合中添加數據
tupleList.add(TupleTest.h());
tupleList.add(TupleTest.h());
//循環集合
tupleList.forEach(each->{
System.out.println(each);
});
}
}
//運行結果爲
FiveTuple{c=hi, d=47, e=11.1, a=generic.Vehicle@817b38, b=generic.Amphibian@4437c4}
FiveTuple{c=hi, d=47, e=11.1, a=generic.Vehicle@13c675d, b=generic.Amphibian@191beef}
- 儘管看上去有些冗長(特別是迭代器創建),但最終還是沒有用過多的代碼就得到了一個相當強大的數據結構。
//構建的模型是一個零售店,它包括走廊,貨架和商品。
public class Product {
private final int id;
private String description;
private double price;
public Product(int id, String description, double price) {
this.id = id;
this.description = description;
this.price = price;
toString();
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", description='" + description + '\'' +
", price=" + price +
'}'+this.getClass().getName();
}
void priceChange(double change) {
price += price;
}
static Generator<Product> generator = new Generator<Product>() {
Random random = new Random(47);
@Override
public Product next() {
return new Product(random.nextInt(1000), "Test", Math.round(random.nextDouble() * 1000) + 0.99);
}
};
}
class Shelf extends ArrayList<Product> {
public Shelf(int initialCapacity) {
Generators.fill(this, Product.generator, initialCapacity);
}
}
class Aisle extends ArrayList<Shelf> {
public Aisle(int nShelves, int initialCapacity) {
for (int i = 0; i < nShelves; i++) {
add(new Shelf(initialCapacity));
}
}
}
class CheckoutStand {}
class Office {}
class Store extends ArrayList<Aisle> {
List<CheckoutStand> checkoutStands = new ArrayList<>();
private Office office = new Office();
public Store(int nAisle, int nShelvs, int initialCapacity) {
for (int i = 0; i < nAisle; i++) {
add(new Aisle(nShelvs, initialCapacity));
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (Aisle aisle:this) {
for (Shelf shelf:aisle) {
for (Product product:shelf) {
builder.append(product);
builder.append("\n");
}
}
}
return builder.toString();
}
public static void main(String[] args) {
System.out.println(new Store(2,2,2));
}
}
//運行結果爲
Product{id=258, description='Test', price=400.99}generic.Product
Product{id=861, description='Test', price=160.99}generic.Product
Product{id=868, description='Test', price=417.99}generic.Product
Product{id=207, description='Test', price=268.99}generic.Product
Product{id=551, description='Test', price=114.99}generic.Product
Product{id=278, description='Test', price=804.99}generic.Product
Product{id=520, description='Test', price=554.99}generic.Product
Product{id=140, description='Test', price=530.99}generic.Product
- 正如我們再 Store.toString() 中看到的,其結果是許多成容器,但是它們是類型安全且可管理的。令人印象深刻之處是組裝這個的模型十分容易,並不會成爲智力挑戰。
擦除的神祕之處
- 當你開始更深入地鑽研泛型時,會發現大量的東西初看起來是沒有意義的。
//儘管你可以聲明 ArrayList.class ,但是你不能聲明 ArrayList<Integer>.class。
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);
}
}
//運行結果爲
true
- new ArrayList<String> 和 new ArrayList<Integer> 很容易被認爲是不同的類型。不同的類型在行爲方面肯定不同,例如,如果 嘗試着將一個 Integer 放入 ArrayList<String> ,所得到的的行爲(將失敗)與把一個Integer 放入 ArrayList<Integer> (將成功)所得到的行爲完全不同。但是上面的程序會認爲它們是相同的類型。
//這是對 謎題的一個補充
class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Particle<T,K>{}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list=new ArrayList<>();
Map<Frob,Fnorkle> map=new HashMap<>();
Quark<Fnorkle> quark=new Quark<>();
Particle<Long,Double> partFactory=new Particle();
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(partFactory.getClass().getTypeParameters()));
}
}
//運行結果爲
[E]
[K, V]
[Q]
[T, K]
- 根據JDK文檔描述, Class.getTypeParameters() 將返回一個 TypeVariable 對象數組,表示有泛型聲明所聲明的類型參數...這好像是在暗示你可能發現參數類型的信息,但是,正如你從輸出所看到的, 你能夠發現的只是用作參數佔位符的表示符忙着並非有用的信息。
- 因此殘酷的顯示是: 在泛型代碼內部,無法獲得任何有關泛型參數類型的信息。
- Java泛型是使用擦除來實現的,這意味着當你使用泛型時,任何具體的類型信息都被擦除了,你唯一知道的就是你在使用一個對象。
- 因此 List<String> 和List<Integer> 在運行時事實上是相同的類型。這兩種形式都被你擦除成它們的 原生類型,即List 。理解擦除以及應該`如何處理它,是你在學習Java泛型時面臨的最大障礙,這也是我們再本章中將要探討的內容。