Thinking In Java Part09(泛型类、泛型方法)

1、接口与类型信息
	interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。
	javap -private C 可以反编译显示出这个类的所有方法,无论private还是匿名
2、泛型
	实现了参数化类型的概念,使代码可以应用于多种类型。术语的意思为:“适用于许多许多的类型”。最初的目的是希望类或方法能够具备最广泛的表达能力,通过解耦类或方法与所使用的类型之间的约束。
	泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
	核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
3、一个元组类库
	元组:将一组对象直接打包存储于其中的一个单一对象。这个容器对象允许读取其中元素,但是不允许向其中存放新的对象(也被称为数据传送对象或信使)。
	元组可以具有任意长度,对象可以是任意不同的类型,但是希望可以指明其类型。
	不用LinkedList实现内部链式存储机制
	public class ContainerTest01 {
	    public static void main(String[] args) {
	        LinkedStack<String> lss = new LinkedStack<>();
	        for(String s : "hello world".split(" ")){
	            lss.push(s);
	        }
	        String s;
	        while((s = lss.pop()) !=null){
	            System.out.println(s);
	        }
	    }
	}

	class LinkedStack<T> {
	    @AllArgsConstructor
	    @Data
	    @NoArgsConstructor
	    private static class Node<U> {
	        U item;
	        Node<U> next;

	        boolean end() {
	            return item == null && next == null;
	        }
	    }
	    private Node<T> top = new Node<T>();
	    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;
	    }
	}
	使用了末端哨兵(end sentinel)来判断堆栈何时为空。在构造LinkedStack的时候创建的空Node就是末端哨兵。之后,每调用一次push方法,就会创建一个Node对象,并将其链接到前一个Node对象。当调用pop方法时,返回top.item并丢弃大当前top所指向的Node,并将top转译到下一个Node,除非碰到了末端哨兵,这时候不再移动top。当到了末端,继续调用pop只会得到null,说明堆栈为空。
4、泛型接口
	泛型应用于接口,生成器:专门负责创建对象的类。是工厂方法设计模式的一种应用,不过使用生成器创建新的对象时,不需要参数,无需额外的信息就知道如何创建新对象。
	一般,一个生成器只定义一个方法,用以产生新的对象。
	public interface Generator<T> {
	    T next();
	}
	生成的类
	public class Coffee {
	    static long counter = 0;
	    final long id = counter++;

	    @Override
	    public String toString() {
	        return getClass().getSimpleName() + " " + id;
	    }
	}
	class Latte extends Coffee {
	}

	class Mocha extends Coffee {
	}

	class Cappuccino extends Coffee {
	}

	class Breve extends Coffee {
	}
	实现Generator<Coffee>接口来随机生成不同类型的Coffee对象
	@NoArgsConstructor
	class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
	    private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Breve.class};
	    private static Random rand = new Random(47);
	    private int size = 0;

	    public CoffeeGenerator(int sz) {
	        size = sz;
	    }

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

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

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

	        @Override
	        public Coffee next() {
	            count--;
	            return CoffeeGenerator.this.next();
	        }
	        @Override
	        public void remove(){
	            throw new UnsupportedOperationException();
	        }
	    }

	    @Override
	    public Iterator<Coffee> iterator() {
	        return new CoffeeIterator();
	    }
        public static void main(String[] args) {
	        CoffeeGenerator coffees = new CoffeeGenerator();
	        for (int i = 0; i < 4; i++) {
	            System.out.println(coffees.next());
	        }
	        for (Coffee c : new CoffeeGenerator(4)) {
	            System.out.println(c);
	        }
	    }
	}
	参数化的Generator接口确保next()返回值是参数的类型,同时还是先了Iterable接口,让他可以在循环语句中用,还有个size来判断何时停止。
	public class Fibonacci implements Generator<Integer> {
	    private int count = 0;

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

	    public int fib(int n) {
	        if (n < 2) {
	            return 1;
	        }
	        return fib(n - 2) + fib(n - 1);
	    }

	    public static void main(String[] args) {
	        Fibonacci fibonacci = new Fibonacci();
	        for (int i = 0; i < 18; i++) {
	            System.out.println(fibonacci.next() + "");
	        }
	    }
	}
	内部使用的int但是参数却是Integer,Java泛型有一个局限性:基本类型无法作为类型参数。
	如果想要一个实现了Iterable的Fibonacci生成器,我们可以重写这个类,但是不一定总有权限,因此,我们可以选择创建一个适配器来实现所需接口。
	现在我们通过继承来创建适配器类。
	class IterableFibonacci extends Fibonacci implements Iterable<Integer> {
	    private int n;

	    public IterableFibonacci(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 IterableFibonacci.this.next();
	            }
	        };
	    }

	}
5、泛型方法
	泛型方法使得该方法能够独立于类而产生变化。基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。如果使用泛型方法可以取代将整个类泛型化,那么就应该使用泛型方法,因为可以使事情更清楚明白。
	当使用泛型类时,必须在创建对象时指定类型参数的值,而使用泛型方法时,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断(type argument inference)。因此,我们可以调用普通方法一样调用泛型方法,就好像泛型方法被无限次重载过。
6、泛型的匿名内部类
    @NoArgsConstructor
    class Customer {
        private static long counter = 1;
        final long id = counter++;

        public static Generator<Customer> generator() {
            return new Generator<Customer>() {
                @Override
                public Customer next() {
                    return new Customer();
                }
            };
        }

        @Override
        public String toString() {
            return "customer:" + id;
        }
    }

    @NoArgsConstructor
    class Teller {
        static long counter = 1;
        final long id = counter++;

        public static Generator<Teller> generator() {
            return new Generator<Teller>() {
                @Override
                public Teller next() {
                    return new Teller();
                }
            };
        }

        @Override
        public String toString() {
            return "Teller:" + id;
        }
    }

    public class BankTeller {
        static void server(Teller t, Customer c) {
            System.out.println(t + " server:" + c);
        }

        public static void main(String[] args) {
            Random random = new Random(47);
            Queue<Customer> customers = new LinkedList<>();
            Generators.fill(customers, Customer.generator(), 15);
            ArrayList<Teller> tellers = new ArrayList<>();
            Generators.fill(tellers, Teller.generator(), 4);
            for (Customer c : customers) {
                server(tellers.get(random.nextInt(tellers.size())), c);
            }
        }
    }

    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;
        }
    }
    Custonmer和Teller类都只有private的构造器,可以强制我们必须使用Generator对象。Customer有一个generator方法,每次执行都会生成一个新的Generator<Customer>对象,我们其实不是很需要多个,Teller就职创建了一个public的generator对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值