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 generator(){
        return new Generator() {
            @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 tellerGenerator(){
        return new Generator() {
            @Override
            public Teller next() {
                return new Teller();
            }
        };
    }
}

public class Generators {
    static  Collection fill(Collection collection,
                                  Generator 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 line=new LinkedList<>();
        //向集合中填充数据
        Generators.fill(line,Customer.generator(),15);
        List 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 对象。
  2. 我们其实不需要多个 Generator 对象,Teller 就只创建了一个 public 的generator 对象。在 main() 方法中可以看到,这两种创建 Generator的方式都在 fill() 中用到了。
  3. 由于Customer 中的 generator() 方法,以及Teller 中的 Generator 对象都声明成了 static 的,所以它们无法作为接口的一部分,因此无法用接口这种特定的惯用法来泛化这二者。尽管如此,它们在 fill() 方法中都工作得很好。

 

 构建复杂模型

  • 泛型的一个重要好处就是能够简单而安全地创建复杂的模型。
//例如,我们可以很容易地创建List 元组 如下

public class TwoTuple {
    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 extends TwoTuple {
    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 extends ThreeTuple {
    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 f() {
        return new TwoTuple<>("hi", 47);
    }
}


public class TupleList  extends ArrayList> {

    public static void main(String[] args) {
        TupleList 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 generator = new Generator() {
        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 {

    public Shelf(int initialCapacity) {
        Generators.fill(this, Product.generator, initialCapacity);
    }
}

class Aisle extends ArrayList {
    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 {

    List 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.class。

public class ErasedTypeEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList().getClass();
        Class c2 = new ArrayList().getClass();
        System.out.println(c1 == c2);
    }
}

//运行结果为

true
  1. new ArrayList 和 new ArrayList 很容易被认为是不同的类型。不同的类型在行为方面肯定不同,例如,如果 尝试着将一个 Integer 放入 ArrayList ,所得到的的行为(将失败)与把一个Integer 放入 ArrayList (将成功)所得到的行为完全不同。但是上面的程序会认为它们是相同的类型。
//这是对 谜题的一个补充


class Frob{}

class Fnorkle{}

class Quark{}

class Particle{}

public class LostInformation {
    public static void main(String[] args) {
        List list=new ArrayList<>();
        Map map=new HashMap<>();
        Quark quark=new Quark<>();
        Particle 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 和List 在运行时事实上是相同的类型。这两种形式都被你擦除成它们的 原生类型,即List 。理解擦除以及应该`如何处理它,是你在学习Java泛型时面临的最大障碍,这也是我们再本章中将要探讨的内容。

 

你可能感兴趣的:(java编程思想)