问题
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.
回答1:
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.
回答2:
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);
}
回答3:
Prior to Java7, constructors don't do type inference, the workaround is to have a static factory method. That's no longer necessary. In Java 7 you can
Foo<List<String>,String> foo = new Foo<>(strings);
Regarding T
and C
, if we have 2 type parameters with constraints between them, there got to be some degree of redundancy. In your example, since one parameter C
totally dictates the another parameter T
, the redundancy seems unbearable. I don't see a solution.
But you probably can feel better if the type parameters are reordered
Foo<String,Bar> foo = new Foo<>(bar);
so we declare String
first; then further provide a Baz<String>
which is Bar
来源:https://stackoverflow.com/questions/8058470/nested-type-parameters-in-java