Thinking in java Chapter15 泛型

Easter79
• 阅读 887
  • 1 与C ++比较
  • 2 简单泛型 泛型 类
  • 3 泛型 接口
  • 4 泛型 方法
  • 5 匿名内部类
  • 6 构建复杂模型
  • 7
  • 8
  • 9
  • 10

“泛型”意思就是:适用于许多许多的类型

1 与C++比较

-------------------------

C++模版,了解泛型的边界所在

2 简单泛型

-----------------------

使用泛型的目的之一: 指定容器持有什么类型,让编译器确保正确性,而不是在运行期发现错误

java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节

2.1 元组

元组允许读取元素,但不能插入新元素,不可以修改元素值,因为元素被设置为final。

元组可以任意长度,可以存储任何类型对象。

创建一个元组,使其返回一组任意类型的对象。

package generics;

import generics.e3.SixTuple;
import net.mindview.util.*;

class Amphibian{} //两栖动物
class Vehicle{}//车辆
public class TupleTest {
    static TwoTuple<String,Integer> f(){
        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);
    }

    static SixTuple<Vehicle,Amphibian,String,Integer,Double,Float> i(){
        return new SixTuple<Vehicle,Amphibian,String,Integer,Double,Float>(new Vehicle(),new Amphibian(),"hi",47,11.1,22.22F);
    }


    public static void main(String[] args) {
        TwoTuple<String,Integer> ttsi = f();
//        ttsi.first = "there"; //编译错误,final
        System.out.println(ttsi);
        System.out.println(g());
        System.out.println(h());
        System.out.println(k());
        System.out.println(i());
    }
}

/*

 */

2.2 一个堆栈类

传统的下推堆栈

11章使用LinkedList

不用LinkedList,实现自己内部的链式存储机制

package generics;

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); 
        // push "Phasers"时,创建对象Node<String>("Phasers",top),top.item == null top.next == null ,
        // 并将创建的Node结点,指向top,即top.item == "Phasers",top.next ==  item和next为null的结点
        
        // push "on"时,创建对象Node<String>("on",top),将"Phasers"的结点,作为"on"的next

        // push "stun!"时,创建对象Node<String>("stun!",top),将"on"的结点,作为"stun!"的next
    }

    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);
    }
}
/*
stun!
on
Phasers
 */

内部类可以访问外部类堆类型参数

package generics.e5;

public class LinkedStack<T> {
    private  class Node{ //将原来的嵌套类 修改 为普通内部类。即Node不能为static
        T item;
        Node next;

        Node() {
            item = null;
            next = null;
        }

        Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }

        boolean end() {
            return item == null && next == null;
        }
    }

    private Node top = new Node(); // End Sentinel 未端哨兵

    public void push(T item) {
        top = new Node(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);
    }
}
/*
stun!
on
Phasers
 */


复习 嵌套内部类 和普通内部类

两种内部类
嵌套类(静态内部类 static):形式上(写法上)和外部类有关系, 其实在逻辑上和外部类并没有直接的关系。可以嵌套类做测试代码main,上线后,将测试类删除

普通内部类:不仅在形式上和外部类有关系(写在外部类的里面), 在逻辑上也和外部类有联系。
1. 内部类对象的创建依赖于外部类对象;

2. 内部类对象持有指向外部类对象的引用。


public class Outer {
    int outerField = 0;
    class Inner{
        void InnerMethod(){
            int i = outerField;
        }
    }
}

javac Outer.java

Outer$Inner.class       Outer.class

javap -v Outer\$Inner.class 
Classfile /Users/erin/JavaProject/thinking_in_java_example/src/main/java/generics/e5/Outer$Inner.class
  Last modified 2019-11-27; size 435 bytes
  MD5 checksum 0e4b5eee2db5f187c00dec5216b5ad8a
  Compiled from "Outer.java"
class generics.e5.Outer$Inner
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #4.#16         // generics/e5/Outer$Inner.this$0:Lgenerics/e5/Outer;
   #2 = Methodref          #5.#17         // java/lang/Object."<init>":()V
   #3 = Fieldref           #18.#19        // generics/e5/Outer.outerField:I
   #4 = Class              #20            // generics/e5/Outer$Inner
   #5 = Class              #23            // java/lang/Object
   #6 = Utf8               this$0
   #7 = Utf8               Lgenerics/e5/Outer;
   #8 = Utf8               <init>
   #9 = Utf8               (Lgenerics/e5/Outer;)V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               InnerMethod
  #13 = Utf8               ()V
  #14 = Utf8               SourceFile
  #15 = Utf8               Outer.java
  #16 = NameAndType        #6:#7          // this$0:Lgenerics/e5/Outer;
  #17 = NameAndType        #8:#13         // "<init>":()V
  #18 = Class              #24            // generics/e5/Outer
  #19 = NameAndType        #25:#26        // outerField:I
  #20 = Utf8               generics/e5/Outer$Inner
  #21 = Utf8               Inner
  #22 = Utf8               InnerClasses
  #23 = Utf8               java/lang/Object
  #24 = Utf8               generics/e5/Outer
  #25 = Utf8               outerField
  #26 = Utf8               I
{
  final generics.e5.Outer this$0; //在内部类Outer$Inner中, 存在一个名字为this$0 , 类型为Outer的成员变量, 并且这个变量是final的。 其实这个就是所谓的“在内部类对象中存在的指向外部类对象的引用”。但是我们在定义这个内部类的时候, 并没有声明它, 所以这个成员变量是编译器加上的
    descriptor: Lgenerics/e5/Outer;
    flags: ACC_FINAL, ACC_SYNTHETIC

  generics.e5.Outer$Inner(generics.e5.Outer); //编译器会为内部类的构造方法添加一个参数, 参数的类型就是外部类的类型。
    descriptor: (Lgenerics/e5/Outer;)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0         //  将局部变量表中的第一个引用变量加载到操作数栈。 这里有几点需要说明。 局部变量表中的变量在方法执行前就已经初始化完成;局部变量表中的变量包括方法的参数;成员方法的局部变量表中的第一个变量永远是this;操作数栈就是执行当前代码的栈。所以这句话的意思是: 将this引用从局部变量表加载到操作数栈。
         1: aload_1 //将局部变量表中的第二个引用变量加载到操作数栈。 这里加载的变量就是构造方法中的Outer类型的参数。
         2: putfield      #1                  // Field this$0:Lgenerics/e5/Outer;使用操作数栈顶端的引用变量为指定的成员变量赋值。 这里的意思是将外面传入的Outer类型的参数赋给成员变量this$0 。 这一句putfield字节码就揭示了, 指向外部类对象的这个引用变量是如何赋值的。
         5: aload_0
         6: invokespecial #2                  // Method java/lang/Object."<init>":()V
         9: return
      LineNumberTable:
        line 5: 0

  void InnerMethod();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=2, args_size=1
         0: aload_0
         1: getfield      #1                  // Field this$0:Lgenerics/e5/Outer;
         4: getfield      #3                  // Field generics/e5/Outer.outerField:I
         7: istore_1
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8
}
SourceFile: "Outer.java"
InnerClasses:
     #21= #4 of #18; //Inner=class generics/e5/Outer$Inner of class generics/e5/Outer


关于内部类如何访问外部类的成员, 主要是通过以下几步做到的:
1 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象的引用;
2 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为1中添加的成员变量赋值;
3 在调用内部类的构造函数初始化内部类对象时, 会默认传入外部类的引用。
https://blog.csdn.net/weixin_39214481/article/details/80372676

2.3 RandomList

package generics;

import java.util.ArrayList;
import java.util.Random;

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);//ArrayList 的 boolean add(E e)
    }

    public int size() {
        return storage.size();
    }

    public T select() {
        return storage.get(rand.nextInt(storage.size()));
    } //ArrayList 的 E get(int index)

    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 < rs.size(); i++)
            System.out.print(rs.select() + " ");
    }
}
/*
brown over fox quick quick dog brown The brown lazy
 */

3 泛型接口

-----------------------

用Coffee和斐波那契数列 分别实现 Generator接口,Iterator接口方式,展示泛型接口。

泛型也可以用于接口,例如生成器,生成器是专门负责创建对象类。

是工厂方法设计模式的一种应用。但工厂方法一般需要参数,

生成器不需要任何参数,一般只定义一个方法。

public interface Generator { T next(); } //接口使用泛型 与 类使用泛型,没有什么区别。

这个例子中,有两种繁殖Coffee的方法, 一种是实现了有next()的Generator, 第二种是实现有遍历功能Iterable 这里两种都是运用了泛型,在Generator和Iterable的泛型类型中加入了Coffee 下面是书上例子,后续对书上例子拆分,清楚显示两种方法。 其中第二个例子中,实现Iterable的Iterator方法,生成Coffee的Iterator方法(hasNext(),next())使用类第一种方法的next方法。

package generics.coffee;

import net.mindview.util.Generator;

import java.util.Iterator;
import java.util.Random;

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;
    }

    class CoffeeItertor implements Iterator<Coffee>{
        int count =size;

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count--;
            return CoffeeGenerator.this.next();
        }

        public void remove() { // 不实现
            throw new UnsupportedOperationException();
        }
    }

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeItertor();
    }

    @Override
    public Coffee next() {
        try {
            return (Coffee) types[rand.nextInt(types.length)].newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public static void main(String[] args) {
        CoffeeGenerator gen = new CoffeeGenerator();
        for (int i =0;i < 5;i ++)
            System.out.println(gen.next());
        System.out.println("=====");
        for (Coffee c: new CoffeeGenerator(5))
            System.out.println(c);
    }
}

将上面的例子拆分下,第一种方法,使用Generator的next方法生成coffee

package generics.coffee;

import net.mindview.util.Generator;

import java.util.Random;

public class CoffeeMethod1Generator implements Generator<Coffee> {
    private Class[] types = {
            Latte.class,
            Mocha.class,
            Cappuccino.class,
            Americano.class,
            Breve.class
    };

    public CoffeeMethod1Generator() {
    }

    private static Random rand = new Random(47);

    @Override
    public Coffee next() {
        try {
            return (Coffee) (types[rand.nextInt(types.length)]).newInstance();// 这句话是关键,随机生成一个指定类型范围内的coffee实例
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        CoffeeMethod1Generator coffee1 = new CoffeeMethod1Generator();
        for (int i = 0; i < 5; i++)
            System.out.println(coffee1.next());
    }
}
/*
Americano 0
Latte 1
Americano 2
Mocha 3
Mocha 4
 */

将上面的例子拆分下,第二种方法

package generics.coffee;

import java.util.Iterator;
import java.util.Random;

public class CoffeeMethod2Iterable implements Iterable<Coffee> {

    private  Class[] types = {
            Latte.class,
            Mocha.class,
            Cappuccino.class,
            Americano.class,
            Breve.class
    };

    private Random rand = new Random(47);



    public Coffee next(){
        try {
            return (Coffee) (types[rand.nextInt(types.length)]).newInstance();
        } catch (Exception e) {
           throw  new RuntimeException(e);
        }
    }
    private int size;

    public CoffeeMethod2Iterable(int sz){size = sz;} //因为使用Iterable接口,所以需要有size的构造方法,作为末端哨兵的功能

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIerator();
    }


    private class CoffeeIerator implements Iterator<Coffee> {
        public int index;
        @Override
        public boolean hasNext() {
            return index <= size;
        }

        @Override
        public Coffee next() {
            index++;
            return CoffeeMethod2Iterable.this.next();// 外部类对象的引用,使用的方法1中的next方法,获取下一个Coffee对象
        }
    }

    public static void main(String[] args) {
        CoffeeMethod2Iterable coffees = new CoffeeMethod2Iterable(5);
        for (Coffee coffee: coffees)
            System.out.println(coffee);
    }
}

以下用另外一个例子,是Generator接口实现,生成斐波那契数列

方法一,使用Generator方法

类型参数为Integer ,基本类型无法作为类型参数

自动打包、自动拆包 可以在基本类型和相应的包装器类型之间进行转换

package generics;

import net.mindview.util.Generator;

public class Fib2 implements Generator<Integer> {// 实现Generator(只有next方法)接口
//public class Fib2 implements Generator<int> {// type argument cannot be of primitive type
    private int count = 0;

    public int fib(int cnt) {
        if (cnt < 2) return 1;// 斐波那契数列,当为0,1时,为1
        return fib(cnt - 2) + fib(cnt - 1);//大于1时,为前两个数字之和
    }

    @Override
    public Integer next() {
        return fib(count++);
    }

    public static void main(String[] args) {
        Fib2 fib2 = new Fib2();
        for (int i = 0; i < 10; i++)
            System.out.print(fib2.next() +  " ");
    }
}

使用实现Iterable接口生成斐波那契生成器,通过使用继承

package generics;

import java.util.Iterator;

public class FibIterator extends Fib2 implements Iterable<Integer> {
    private int n;

    public FibIterator(int count) {
        n = count;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return n > 0;
            }

            @Override
            public Integer next() {
                n--;
                return FibIterator.this.next();// 继承通过Generator(生成next方法),创建适配器,外部类类对象的引用方法
            }
        };
    }

    public static void main(String[] args) {
//        FibIterator fibIterator = new FibIterator(10);
//        for (Integer integer: fibIterator)
        for (Integer integer: new FibIterator(10)) //foreach语句,需要边界值。用于hasNext知道何时返回false
            System.out.print(integer + " ");
        
    }
}

使用实现Iterable接口生成斐波那契生成器,通过使用组合

e7 使用组合代替继承

package generics.e7;

import generics.Fib2;

import java.util.Iterator;

public class IterableFibonacci implements Iterable<Integer> {
    Fib2 fib2 = new Fib2();// 通过组合方式

    private int count;

    public IterableFibonacci(int cnt){count = cnt;}

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return count >0;
            }

            @Override
            public Integer next() {
                count--;
                return fib2.next();
            }
        };
    }

    public static void main(String[] args) {
        for (int i : new IterableFibonacci(10))
            System.out.print(i + " ");
    }
}



作业8,与coffee例子一样
package generics.e8;

import net.mindview.util.Generator;

import java.util.Iterator;
import java.util.Random;

class StoryCharacter{
    private static long counter;
    private final long id = counter++;
    public String toString(){
        return getClass().getSimpleName() + " " +id;
    }
}

class GoodGuy extends StoryCharacter{
    public String toString(){
        return super.toString() + " is a good guy";
    }
}

class BadGuy extends StoryCharacter{
    public String toString(){
        return super.toString() + " is a bad guy";
    }
}

class Morton extends BadGuy{}
class Frank extends BadGuy{}
class Harmonica extends GoodGuy{}
class Cheyenne extends GoodGuy{}

class CharacterGenerator implements Generator<StoryCharacter>,Iterable<StoryCharacter>{
    private Class[] types = {
            Morton.class,
            Frank.class,
            Harmonica.class,
            Cheyenne.class
    };

    private Random rand = new Random(47);


    // for iteratation;
    private int size;
    public CharacterGenerator(){}

    public CharacterGenerator(int count){
        size = count;
    }


    @Override
    public Iterator<StoryCharacter> iterator() {
        return new Iterator<StoryCharacter>() {
            @Override
            public boolean hasNext() {
                return size > 0;
            }

            @Override
            public StoryCharacter next() {
                size--;
                return CharacterGenerator.this.next();
            }
        };
    }

    @Override
    public StoryCharacter next() {
        try {
            return (StoryCharacter)(types[rand.nextInt(types.length)]).newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

public class E08_CharacterGenerator {
    public static void main(String[] args) {
        CharacterGenerator cg= new CharacterGenerator();
        for (int i = 0; i < 5; i++)
            System.out.println(cg.next());

        System.out.println("======");

        for (StoryCharacter sc: new CharacterGenerator(5))
            System.out.println(sc);


    }
}

4 泛型方法

-----------------------

泛型方法所在的类可以是泛型类,也可以不是泛型类,

并且泛型标识符可以完全不一样,也就是说泛型方法和泛型类无关。

普通static方法无法访问泛型类的类型参数,如果要是使用泛型就要定义成泛型静态方法

定义泛型方法只需要将泛型参数列表置于返回值前 public void f(T x)

package generics;

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);
    }
}


package generics.e10;

public class GenericMethods {
    public <B,C> void f(String a,B b,C c) {//多个类型不同参数,非参数化类型
        System.out.println(a.getClass().getName());
        System.out.println(b.getClass().getName());
        System.out.println(c.getClass().getName());
    }

    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("",1,gm);
    }
}

4.1 杠杆利用类型参数推断

使用泛型方法时编译期会通过类型参数推断来为我们找出具体类型,而不必自己声明时什么类型

类型推断只对赋值操作有效,其它时候不起作用 JDK8不存在例子中编译问题

显示类型说明

package generics;

import net.mindview.util.New;
import typeinfo.pets.*;

import java.util.*;

public class LimitsOfInference {
    static void
    f(Map<Person, List<? extends Pet>> petPeople) {
    }

    public static void main(String[] args) {
        f(New.<Person, List<? extends Pet>>map()); // 显示的类型说明
        // 如果是在定义该方法的类的内部,需要this.
        // static方法,需要 ClassName.
        f(New.map()); // Does not compile JDK8 正常
    }
}

4.2 可变参数与泛型方法

泛型方法与可变参数列表能够很好地共存

下面的方法展示了和类库java.util.Arrays.asList()方法相同的功能

package generics;
//: generics/GenericVarargs.java

import java.util.*;

public class GenericVarargs {
    public static <T> List<T> makeList(T... args) {
        List<T> result = new ArrayList<T>();
        for (T item : args)
            result.add(item);
        return result;
    }

    public static void main(String[] args) {
        List<String> ls = makeList("A");
        System.out.println(ls);
        ls = makeList("A", "B", "C");
        System.out.println(ls);
        ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
        System.out.println(ls);
    }
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
*///:~

4.3 用于Generator的泛型方法

package generics;
//: generics/Generators.java
// A utility to use with Generators.
import generics.coffee.*;
import java.util.*;
import net.mindview.util.*;

public class Generators {
    public static <T> Collection<T>
    fill(Collection<T> coll, Generator<T> gen, int n) { //Generator的泛型方法
        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,
*///:~

4.4 一个通用的Generator

package generics;

import net.mindview.util.Generator;

public class BasicGenerator<T> implements Generator<T> {
    private Class<T> type;

    public BasicGenerator(Class<T> type){this.type = type;}

    @Override
    public T next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Generator<T> create(Class<T> type){
        return new BasicGenerator<T>(type);
    }

}



package generics;

public class CountedObject {
    private static long counter = 0;
    private final long id = counter++;
    public long id(){return id;}
    public String toString(){
        return "CountedObject " + id;
    }
}


package generics;

import net.mindview.util.Generator;

public class BasicGeneratorDemo {
    public static void main(String[] args) {
        Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        //  BasicGenerator<CountedObject> gen = new BasicGenerator(CountedObject.class); 等价
        for (int i = 0; i < 5;i ++)
            System.out.println(gen.next());
    }
}

4.5 简化元组的使用

4.6一个Set实用工具

5 匿名内部类

------------------------

泛型用于内部类及匿名内部类。

示例使用匿名内部类实现Generator接口

package generics;

import net.mindview.util.Generator;

import java.util.*;

class Customer {
    private static long counter = 1;
    private final long id = counter++;

    //私有化构造方法,只能通过Generator 获取实例
    private Customer() {
    }

    public String toString() {
        return "Customer " + id;
    }

    // A method to produce Generator objects:
    // Customer 对象生成器
    // Generator 每次调用 都会创建 一个 Generator对象,但这不是必要的。
    public static Generator<Customer> generator() {
        return new Generator<Customer>() {
            @Override
            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:
    // 匿名内部类2
    // 单例Generator对象:
    // 可以对比Customer的generator方法 这里只会创建一个generator实例
    public static Generator<Teller> generator =
            new Generator<Teller>() {
                @Override
                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)//遍历line,随机取出teller与 Customer 按序输出
            serve(tellers.get(rand.nextInt(tellers.size())), c);
    }
}

6 构建复杂模型

-------------------------

1

------------------

1

------------------

1

------------------
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java 泛型详解
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。本文参考java泛型详解、Java中的泛型方法、java泛型详解1\.概述泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。什么是泛型?为什么要使用泛型?泛型,即“参数化类型”。一提到参数,最熟
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
Java的泛型详解(一)
Java的泛型详解(一)编写的代码可以被不同类型的对象所重用。因为上面的一个优点,泛型也可以减少代码的编写。1|2泛型的使用简单泛型类publicclassPair{privateTfirst;privateTsecond;publicPair(){firstnull;secondnull;
Stella981 Stella981
3年前
20175209 《Java程序设计》第八周学习总结
20175209《Java程序设计》第八周学习总结一、教材知识点总结1.泛型1.泛型类声明:格式classPeople<EPeople是泛型类名称E是泛型列表,可以是任何对象或接口,但不能是基本类型数据
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
Java泛型一览笔录
1、什么是泛型?泛型(Generics)是把类型参数化,运用于类、接口、方法中,可以通过执行泛型类型调用分配一个类型,将用分配的具体类型替换泛型类型。然后,所分配的类型将用于限制容器内使用的值,这样就无需进行类型转换,还可以在编译时提供更强的类型检查。2、泛型有什么用?泛型主要有两个好处:(1)消除显
可莉 可莉
3年前
20175209 《Java程序设计》第八周学习总结
20175209《Java程序设计》第八周学习总结一、教材知识点总结1.泛型1.泛型类声明:格式classPeople<EPeople是泛型类名称E是泛型列表,可以是任何对象或接口,但不能是基本类型数据
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k