泛型的目标之一就是能够编写尽可能广泛应用的代码。
为了实现这一点,我们需要各种途径来放松对我们的代码将要作用的类型所做的限制,同时不丢失静态类型检查的好处。即写出更加泛化的代码。
Java泛型看起来是向这个方向迈进了一步。但是还是有一定的限制:
(1)当你在编写或使用只是持有对象<T>的泛型时,这些代码可以适用于任何类型。这些代码就可以真正应用于任何地方,因此相当泛化。
(2)当要在泛型类型上执行操作时,就会产生问题,因为擦除要求指定可能会用到的泛型类型的边界,以安全的调用代码中的泛型对象上的具体方法。
这是对泛化的明显限制。因为必须限制你的泛型类型,使它们继承特定类或实现特定接口。
某些编程语言(Python、C++)提供一种解决方案称为潜在类型机制,它只要求泛型代码实现某个方法的子集,而不是特定类或接口。
尽管Java不支持潜在类型机制,但这并不意味着有界泛型代码不能在不同的类型层次结构之间应用,只不过需要付出一些额外的努力。
(0)以下面例子作为原型:
1 interface Performs { 2 void speak(); 3 void sit(); 4 } 5 class Dog implements Performs { 6 @Override public void speak() { System.out.println("Dog Speaking..."); } 7 @Override public void sit() { System.out.println("Dog Sitting..."); } 8 public void run() { System.out.println("Dog Running..."); } 9 } 10 class Robot implements Performs { 11 @Override public void speak() { System.out.println("Robot Speaking..."); } 12 @Override public void sit() { System.out.println("Robot Sitting..."); } 13 public void calc() { System.out.println("Robot Calculating..."); } 14 } 15 class Actions { 16 public static void perform(Performs p) { 17 p.speak(); 18 p.sit(); 19 } 20 } 21 public class DogsAndRobots { 22 public static void main(String[] args) { 23 Actions.perform(new Dog()); 24 Actions.perform(new Robot()); 25 } 26 }
(1)反射
1 import java.lang.reflect.InvocationTargetException; 2 import java.lang.reflect.Method; 3 4 class Dog1 { 5 public void speak() { System.out.println("Dog1 Speaking..."); } 6 public void sit() { System.out.println("Dog1 Sitting..."); } 7 public void run() { System.out.println("Dog1 Running..."); } 8 } 9 class Robot1 { 10 public void speak() { System.out.println("Robot1 Speaking..."); } 11 public void sit() { System.out.println("Robot1 Sitting..."); } 12 public void calc() { System.out.println("Robot1 Calculating..."); } 13 } 14 class Actions1 { 15 public static void perform(Object obj) { 16 Class<?> objClass = obj.getClass(); 17 try { 18 Method speak = objClass.getMethod("speak"); 19 speak.invoke(obj); 20 Method sit = objClass.getMethod("sit"); 21 sit.invoke(obj); 22 } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 public class DogsAndRobots1 { 28 public static void main(String[] args) { 29 Actions1.perform(new Dog1()); 30 Actions1.perform(new Robot1()); 31 } 32 }
(2)将一个方法应用于序列 (apply方法可以接受任何实现Iterable接口的事物,然而,这样的代码还是不够泛化。)
1 import java.lang.reflect.InvocationTargetException; 2 import java.lang.reflect.Method; 3 import java.util.ArrayList; 4 import java.util.List; 5 6 class Dog2 { 7 public void speak() { System.out.println("Dog2 Speaking..."); } 8 public void sit() { System.out.println("Dog2 Sitting..."); } 9 public void run(int length) { System.out.println("Dog2 runs " + length + " miles."); } 10 } 11 12 class SmartDog extends Dog2 {} 13 14 class Actions2 { 15 // PECS 16 public static <T, S extends Iterable<? extends T>> void apply(S seq, Method f, Object... args) { 17 seq.forEach(p -> { 18 try { 19 f.invoke(p, args); 20 } catch (IllegalAccessException | InvocationTargetException e) { 21 e.printStackTrace(); 22 } 23 }); 24 } 25 } 26 27 class FilledList<T> extends ArrayList<T> { 28 // PECS 29 public FilledList(Class<? extends T> type, int size) throws Exception { 30 for(int i = 0; i < size; i++) { 31 add(type.newInstance()); 32 } 33 } 34 } 35 36 public class DogsAndRobots2 { 37 public static void main(String[] args) throws Exception { 38 List<Dog2> dog2s = new ArrayList<>(); 39 for (int i = 0; i < 3; i++) { 40 dog2s.add(new Dog2()); 41 } 42 Actions2.apply(dog2s, Dog2.class.getMethod("speak")); 43 Actions2.apply(new FilledList<>(SmartDog.class, 2), SmartDog.class.getMethod("run", int.class), 1); 44 } 45 }
(3)用适配器仿真潜在类型机制
1 import java.util.*; 2 3 interface Addable<T> { void add(T t); } 4 5 interface Generator<T> { T next(); } 6 7 class SimpleQueue<T> implements Iterable<T> { 8 private LinkedList<T> storage = new LinkedList<T>(); 9 public void add(T t) { storage.offer(t); } 10 public T get() { return storage.poll(); } 11 public Iterator<T> iterator() { 12 return storage.iterator(); 13 } 14 } 15 16 class Dog3 { 17 public void speak(){ System.out.println("Dog2 Speaking..."); } 18 public void sit() { System.out.println("Dog2 Sitting..."); } 19 public void run(int length) { System.out.println("Dog2 runs " + length + " miles."); } 20 } 21 22 class BigDog extends Dog3 {} 23 class SmallDog extends Dog3 {} 24 25 class Fill { 26 // Classtoken version: 27 public static <T> void fill(Addable<T> addable, Class<? extends T> classToken, int size) { 28 for (int i = 0; i < size; i++) 29 try { 30 addable.add(classToken.newInstance()); 31 } catch (Exception e) { 32 throw new RuntimeException(e); 33 } 34 } 35 36 // Generator version: 37 public static <T> void fill(Addable<T> addable, Generator<T> generator, int size) { 38 for (int i = 0; i < size; i++) 39 addable.add(generator.next()); 40 } 41 } 42 43 // To adapt a base type, you must use composition. 44 // Make any Collection Addable using composition: 45 class AddableCollectionAdapter<T> implements Addable<T> { 46 private Collection<T> c; 47 48 public AddableCollectionAdapter(Collection<T> c) { 49 this.c = c; 50 } 51 52 public void add(T item) { 53 c.add(item); 54 } 55 } 56 57 // A Helper to capture the type automatically: 58 class Adapter { 59 public static <T> Addable<T> collectionAdapter(Collection<T> c) { 60 return new AddableCollectionAdapter<>(c); 61 } 62 } 63 64 // To adapt a specific type, you can use inheritance. 65 // Make a SimpleQueue Addable using inheritance: 66 class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T> { 67 public void add(T item) { super.add(item); } 68 } 69 70 public class DogsAndRobots3 { 71 public static void main(String[] args) { 72 List<Dog3> dog3s = new ArrayList<>(); 73 // Adapt a Collection: 74 Fill.fill(new AddableCollectionAdapter<>(dog3s), BigDog.class, 3); 75 // Helper method captures the type: 76 Fill.fill(Adapter.collectionAdapter(dog3s), SmallDog.class, 2); 77 for(Dog3 elem : dog3s) { 78 System.out.println(elem.getClass().getSimpleName()); 79 } 80 System.out.println("----------------------"); 81 // Use an adapted class: 82 AddableSimpleQueue<Dog3> dog3Queue = new AddableSimpleQueue<>(); 83 Fill.fill(dog3Queue, BigDog.class, 4); 84 Fill.fill(dog3Queue, SmallDog.class, 1); 85 for(Dog3 elem : dog3Queue) { 86 System.out.println(elem.getClass().getSimpleName()); 87 } 88 // BigDog 89 // BigDog 90 // BigDog 91 // SmallDog 92 // SmallDog 93 // ---------------------- 94 // BigDog 95 // BigDog 96 // BigDog 97 // BigDog 98 // SmallDog 99 } 100 }