// generics/Fill2.java // Using adapters to simulate latent typing. // {main: Fill2Test} import generics.coffee.*; import java.util.*; import java.util.function.*; interface Addable { void add(T t); } public class Fill2 { // Classtoken version: public static void fill(Addable addable, Class classToken, int size) { for(int i = 0; i < size; i++) try { addable.add(classToken.newInstance()); } catch(InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } // Supplier version: public static void fill(Addable addable, Supplier generator, int size) { for(int i = 0; i < size; i++) addable.add(generator.get()); } } // To adapt a base type, you must use composition. // Make any Collection Addable using composition: class AddableCollectionAdapter implements Addable { private Collection c; public AddableCollectionAdapter(Collection c) { this.c = c; } @Override public void add(T item) { c.add(item); } } // A Helper to capture the type automatically: class Adapter { public static Addable collectionAdapter(Collection c) { return new AddableCollectionAdapter<>(c); } } // To adapt a specific type, you can use inheritance. // Make a SimpleQueue Addable using inheritance: class AddableSimpleQueue extends SimpleQueue implements Addable { @Override public void add(T item) { super.add(item); } } class Fill2Test { public static void main(String[] args) { // Adapt a Collection: List carrier = new ArrayList<>(); Fill2.fill( new AddableCollectionAdapter<>(carrier), Coffee.class, 3); // Helper method captures the type: Fill2.fill(Adapter.collectionAdapter(carrier), Latte.class, 2); for(Coffee c: carrier) System.out.println(c); System.out.println("----------------------"); // Use an adapted class: AddableSimpleQueue coffeeQueue = new AddableSimpleQueue<>(); Fill2.fill(coffeeQueue, Mocha.class, 4); Fill2.fill(coffeeQueue, Latte.class, 1); for(Coffee c: coffeeQueue) System.out.println(c); } } /* Output: Coffee 0 Coffee 1 Coffee 2 Latte 3 Latte 4 ---------------------- Mocha 5 Mocha 6 Mocha 7 Mocha 8 Latte 9 */