Java編程思想__泛型(二)

  • 泛型還可以應用於內部類以及匿名內部類。
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}
  1. Customer 和 Teller 類都只有 private 的構造器,這可以強制你必須是用 Generator 對象。 Customer 有一個 generator() 方法,每次執行它都會生成一個新的 Generator<Customer> 對象。
  2. 我們其實不需要多個 Generator 對象,Teller 就只創建了一個 public 的generator 對象。在 main() 方法中可以看到,這兩種創建 Generator的方式都在 fill() 中用到了。
  3. 由於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}
  1. 儘管看上去有些冗長(特別是迭代器創建),但最終還是沒有用過多的代碼就得到了一個相當強大的數據結構。
//構建的模型是一個零售店,它包括走廊,貨架和商品。

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
  1. 正如我們再 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
  1. 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]
  1. 根據JDK文檔描述, Class.getTypeParameters() 將返回一個 TypeVariable 對象數組,表示有泛型聲明所聲明的類型參數...這好像是在暗示你可能發現參數類型的信息,但是,正如你從輸出所看到的, 你能夠發現的只是用作參數佔位符的表示符忙着並非有用的信息。
  2. 因此殘酷的顯示是: 在泛型代碼內部,無法獲得任何有關泛型參數類型的信息
  3. Java泛型是使用擦除來實現的,這意味着當你使用泛型時,任何具體的類型信息都被擦除了,你唯一知道的就是你在使用一個對象。
  4. 因此 List<String> 和List<Integer> 在運行時事實上是相同的類型。這兩種形式都被你擦除成它們的 原生類型,即List 。理解擦除以及應該`如何處理它,是你在學習Java泛型時面臨的最大障礙,這也是我們再本章中將要探討的內容。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章