Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Type Parameters in Java

This is an example which I made up to be a simplification of my real code, so I apologize if it is a little contrived. What I would like to do is to effectively get two type parameters out of a single nested type argument. I'm pretty sure this is impossible, but I thought I'd give it a shot.

//Not legal java code
public class Foo<C extends Collection<T>> { //where T is another type parameter
    private C coll;

    public Foo(C coll) {
        this.coll = coll;
    }

    public void add(T elem){
        this.coll.add(elem);
    }
    //UPDATED TO ADD GETTER
    /**
     * I may need to retrieve the collection again, or pass it
     * on to another function that needs the specific C type
     */
    public C getColl(){
        return coll;
    }
}
...
List<String> strings = new ArrayList<String>();
Foo<List<String>> foo = new Foo<List<String>>(strings);
foo.add("hello");

I know that I could do it by adding another type parameter:

public class Foo<C extends Collection<T>,T>

but then I have to add the redundant:

Foo<List<String>,String> foo = new Foo<List<String>,String>(strings);

And in my real world case, my generics can sometimes be specified in the implements clause like

public class Bar implements Baz<String>

Having to specify that second type parameter is even more painful then, because it feels like it throws the implementation details in my face. Having to say

Foo<Bar,String>

when there is a relationship between String and Bar already, just seems inelegant. I get that its Java, so that goes with the territory, but just curious if there was a solution for this.

like image 635
Russell Leggett Avatar asked Nov 08 '11 23:11

Russell Leggett


2 Answers

It's not possible and I don't think it's ideal anyway because there is nothing in your existing class that requires invariance.

Foo<T,C extends Collection<T>>

could more generally be

Foo<T,C extends Collection<? super T>>

if the only reason to have T is to allow mutation of the collection.

Note, if you're concerned about having to specify two type parameters frequently, you can create a shallow subclass:

class DerivedFoo<T> extends Foo<Collection<T>,T>

and you can use factory methods to avoid having to double-specify at creation time

public static <T> Foo<Collection<T>,T> fromCollection(Collection<T> c)

You can also abstract the interface into an interface to get the benefits of concise types that you get with DerivedFoo above.

like image 68
Mike Samuel Avatar answered Sep 20 '22 12:09

Mike Samuel


Why wouldn't you just use T as your only type parameter, as in:

public class Foo<T> { //where T is another type parameter
private Collection<T> coll;

public Foo(Collection<T> coll) {
    this.coll = coll;
}

public void add(T elem){
    this.coll.add(elem);
}
like image 21
antlersoft Avatar answered Sep 19 '22 12:09

antlersoft