javagenericsgeneric-type-argument

In Java, can I reuse the generics types from an interface parameter to create a different class which also requires generic types? And if so, how?


Here is an overview of the Java code I have:

// An interface and an implementation class:
public interface MyInterface<T1, T2> { ... }
public class MyImplementation implements MyInterface<int, String> { ... }

// Another class
public class MyClass<T3, T4> { ... }

// Function I want to call
void doStuff(MyInterface i) {
  MyClass<int, String> n;
}

// I want to call the function like this:
MyInterface mi = new MyImplementation();
doStuff(mi);

What I can't figure out is if I can get MyClass<int, String> n; to somehow use the generic types from the MyImplementation class passed in to doStuff()? In this case, n would automatically use <int, String> because that's what MyImplementation uses.


Solution

  • Yes, you can.

    Let's move away from nebulous hypotheticals and take real classes: Collection<T>, Map<K, V>, and Function<F, T>. Let's say you want to write a method in the Map type (or interface, doesn't matter, a signature is a signature) that takes a 'key converter' (a thing that converts Ks into something else), returning a collection of the something-else, which consists of each key in the map, thrown through the converter, and added to a collection.

    class MapImpl<K, V> implements Map<K, V> {
        public <T> Collection<T> convertKeys(Function<K, T> converter) {
            List<T> out = new ArrayList<T>();
            for (K key : keySet()) out.add(converter.apply(key));
            return out;
        }
    }
    

    A lot of concepts are being used here:

    Going back to your code, it might look like:

    public <K, V> void doStuff(MyInterface<K, V> i) {
        MyClass<K, V> n;
    }
    

    NB: Remember, generics link things. That final snippet is simply saying: There is a link between the first typearg of the MyInterface part of the 'i' parameter's type, and the first typearg of the MyClass part of the 'n' local variable. I don't know what that type is. I do know it is the same type. Generics are completely useless unless the typevar occurs in 2 or more places.

    NB2: If you then want to get real fancy, you start thinking about co/contra/invariance. For example, in the key converter story, if you have a converter that can convert any object into something else that'd be cool too. In fact, a converter that can convert either Ks, or any supertype of Ks, that'd all be suitable. So, really, you end up with: public <T> Collection<T> convertKeys(Function<? super K, ? extends T> converter) {} - but that kind of advanced variance engineering is a nice bonus, feel free to skip those bits in your source until you run into trouble because you didn't take it into consideration.