泛型可以应用于同一个类,该类可以针对多种类型使用,例如构建一个RedisTemplateService 组件,用于处理当前应用中所有对象的缓存操作。这篇文章主要介绍泛型应用于接口、方法和匿名内部类的一些知识点和使用案例,也包括《Java编程思想》中对应的练习题的解读。
泛型接口泛型应用于接口,是工厂方法设计模式 的一种应用。我使用《Java编程思想》中的例子进行了练习。
下面这个例子中,CoffeeGenerator用于生成随机的Coffee对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package org.java.learn.generics.coffee;import org.apache.commons.lang3.RandomUtils;import org.java.learn.util.Generator;import java.util.Iterator;public class CoffeeGenerator implements Generator <Coffee >, Iterable <Coffee > { private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class}; private int size = 0 ; public CoffeeGenerator () { } public CoffeeGenerator (int size) { this .size = size; } @Override public Coffee next () { try { return (Coffee) types[RandomUtils.nextInt(0 , types.length-1 )].newInstance(); } catch (Exception e) { throw new RuntimeException(); } } 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 gen = new CoffeeGenerator(); for (int i = 0 ; i < 5 ; i++) { System.out.println(gen.next()); } for (Coffee coffee: new CoffeeGenerator(5 )) { System.out.println(coffee); } } }
再看一个例子,使用Generator接口生成Fibonacci数列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package org.java.learn.generics.coffee;import org.java.learn.util.Generator;public class Fibonacci implements Generator <Integer > { private int count = 0 ; @Override public Integer next () { return fib(count++); } private 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.print(fibonacci.next() + " " ); } } }
如果希望将这个Fibonacci生成器用于循环语句,书中的例子用的是继承Fibonacci,写一个IterableFibonacci类,该类实现了Iterable接口。在练习7中,作者提示可以使用“组合代替继承”实现同样的功能,我尝试自己做了下,这是我的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package org.java.learn.generics;import java.util.Iterator;public class IterableFibonacci2 implements Iterable <Integer > { private int n; private Fibonacci fibonacci; public IterableFibonacci2 (int n, Fibonacci fibonacci) { this .n = n; this .fibonacci = fibonacci; } @Override public Iterator<Integer> iterator () { return new Iterator<Integer>() { @Override public boolean hasNext () { return n > 0 ; } @Override public Integer next () { n--; return fibonacci.next(); } @Override public void remove () { throw new UnsupportedOperationException(); } }; } public static void main (String[] args) { for (int i: new IterableFibonacci2(18 , new Fibonacci())) { System.out.print(i + " " ); } } }
上面三个例子中,提到了两种设计模式:工厂方法设计模式 和适配器模式 。 在泛型中,基本类型无法作为类型参数,但是Java提供了自动打包和拆包的功能; 泛型方法 知识点总结如果使用泛型方法可以取代将整个类(或接口)泛型化,那么就应该只使用泛型方法; static方法要使用泛型能力,就必须成为泛型方法; 类型推断 :这是编译器的特性。在使用泛型类的时候,必须在创建对象的时候指定类型参数的值,但是在使用泛型方法时候,不必指明参数类型。泛型方法与可变参数可以一起使用 例子1:使用Generator的泛型方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package org.java.learn.generics;import org.java.learn.generics.coffee.Coffee;import org.java.learn.generics.coffee.CoffeeGenerator;import org.java.learn.util.Generator;import java.util.ArrayList;import java.util.Collection;public 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; } public static void main (String[] args) { Collection<Coffee> coffees = fill(new ArrayList<>(), new CoffeeGenerator(), 4 ); for (Coffee coffee : coffees) { System.out.println(coffee); } Collection<Integer> numers = fill(new ArrayList<>(), new Fibonacci(), 12 ); for (int i : numers) { System.out.print(i + " " ); } } }
例子2:一个通用的Generator下面这个例子,是一个通用的生成器,只需要传入指定的类型,就可以生成对应类型的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package org.java.learn.util;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) { e.printStackTrace(); throw new RuntimeException(e); } } public static <T> Generator<T> create (Class<T> type) { return new BasicGenerator<>(type); } }
上面这段代码可以创建什么类的对象呢?(1)public的类;(2)含有默认构造器的类;这里给出一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.java.learn.generics;public class CountedObject { private static long counter = 0 ; private final long id = counter++; public long getId () { return id; } @Override public String toString () { return "CounteredObject " + id; } }
然后再给出一个使用上述构造器的例子,书中的例子是使用BasicGenerator的create()方法,我这里还实现了练习14中提到的方法,参见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package org.java.learn.generics;import org.java.learn.util.BasicGenerator;import org.java.learn.util.Generator;public class BasicGeneratorDemo { public static void main (String[] args) { Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class); for (int i = 0 ; i < 5 ; i++) { System.out.println(gen.next()); } Generator<CountedObject> generator = new BasicGenerator<>(CountedObject.class); for (int i = 0 ; i < 5 ; i++) { System.out.println(generator.next()); } } }
例子3:简化元组的使用之前的一篇文章里,已经实现过TwoTuple、ThreeTuple等工具类,但是使用的时候还不太方便,这里利用泛型方法实现一个工具类,可以简化元组的使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.java.learn.util.tuple;public class Tuple { public static <A, B> TwoTuple<A, B> tuple (A a, B b) { return new TwoTuple<>(a, b); } public static <A, B, C> ThreeTuple<A, B, C> tuple (A a, B b, C c) { return new ThreeTuple<>(a, b, c); } public static <A, B, C, D> FourTuple<A, B, C, D> tuple (A a, B b, C c, D d) { return new FourTuple<>(a, b, c, d); } }
这个工具类的使用例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package org.java.learn.util.tuple;import org.java.learn.generics.coffee.Breve;import org.java.learn.generics.coffee.Cappuccino;import static org.java.learn.util.tuple.Tuple.*;public class TupleTest { static TwoTuple<String, Integer> f () { return tuple("hi" , 47 ); } static TwoTuple f2 () { return tuple("hi" , 47 ); } static ThreeTuple<Breve, String, Integer> g () { return tuple(new Breve(), "hi" , 44 ); } static FourTuple<Cappuccino, Breve, String, Integer> h () { return tuple(new Cappuccino(), new Breve(), "hi" , 447 ); } public static void main (String[] args) { TwoTuple<String, Integer> ttsi = f(); System.out.println(ttsi); System.out.println(f2()); System.out.println(g()); System.out.println(h()); TwoTuple<String, Integer> ttsi2 = f2(); } }
例子4:一个Set实用工具书中提供了一个Sets工具类,用于实现常用的集合操作,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package org.java.learn.util;import java.util.HashSet;import java.util.Set;public class Sets { public static <T> Set<T> union (Set<T> a, Set<T> b) { Set<T> result = new HashSet<>(a); result.addAll(b); return result; } public static <T> Set<T> intersection (Set<T> a, Set<T> b) { Set<T> result = new HashSet<>(a); result.retainAll(b); return result; } public static <T> Set<T> difference (Set<T> superset, Set<T> subset) { Set<T> result = new HashSet<>(superset); result.removeAll(subset); return result; } public static <T> Set<T> complement (Set<T> a, Set<T> b) { return difference(union(a, b), intersection(a, b)); } }
# 总结本节涉及的知识点:泛型接口、泛型方法 本节练习用的代码:LearnJava Thoughts, stories and ideas.