What am I doing wrong with Java generics in this interface and method?
EDIT: There is another wrinkle in here that I missed that turns out to make a big difference. The开发者_如何学C method signature of doAnotherThing
is instead the following:
<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
return foo.get(0).doSomething();
}
Disregard the fact that it is a List
, just pay attention to the fact that List
is a generic class/interface. I'm calling the method like so:
doAnotherThing(new ArrayList<FooImpl>);
So, I have a class and interface defined like so:
abstract class Bar {
// some neat stuff
}
class BarImpl extends Bar {
// some cool stuff
}
interface Foo<T extends Bar> {
T doSomething();
}
class FooImpl implements Foo<BarImpl> {
BarImpl doSomething() {
// Does something awesome
}
}
This is all fine and dandy and works great.
Now, I have a method like so:
<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
return foo.get(0).doSomething();
}
This method is a generic method in a completely different class and is not part of the chain above.
However, when I try to use this method in the following manner, I'm getting an error saying that the types don't match:
doAnotherThing(new FooImpl());
FooImpl
implements Foo<T>
, so I don't see how this is an error? Maybe I'm misunderstanding something? Thanks
Answer changed to reflect clarification in question
The complilation error (type mismatch) is actually perfectly correct. Note that the method definition says the parameter is a List<Foo<T>>
. This means that the list can contain any Foo<T>
, and the method must even be able to add any object that implements Foo<T>
. And that's not the case when you give it a List<FooImpl>
, because that is only allowed to contain instances of FooImpl
. This works:
doAnotherThing(new ArrayList<Foo<BarImpl>>());
Mixing generics and polymorphism quickly leads to very complex scenarios, so it should be done sparingly.
Works for me. I suspect whatever's causing the error, it isn't in the version you've got up above. Does the code below (all one file, FooBarBaz.java
) compile for you? The only change I had to make to your code was to make FooImpl.doSomething()
public. (Oh, and make it return something. :)
)
public class FooBarBaz {
<T extends Bar> T doAnotherThing(Foo<T> foo) {
return foo.doSomething();
}
public static void main(String[] args) {
new FooBarBaz().doAnotherThing(new FooImpl());
}
}
abstract class Bar {
// some neat stuff
}
class BarImpl extends Bar {
// some cool stuff
}
interface Foo<T extends Bar> {
T doSomething();
}
class FooImpl implements Foo<BarImpl> {
public BarImpl doSomething() {
return null;
}
}
Works fine for me with IDEA 9, JDK 1.6 on Mac OS 10.6.5.
Let's assume BarImpl extends Bar
, which I believe you meant from the beginning.
How does the augmented Foo
interface look like?
Is it:
interface Foo<T extends Bar> {
T doSomething();
T doAnotherThing(Foo<T> foo);
}
?
In such a case everything works with the following impl:
class FooImpl implements Foo<BarImpl> {
public BarImpl doSomething() {
return null;
}
public BarImpl doAnotherThing(Foo<BarImpl> foo) {
return null;
}
}
Now, there might be a problem if you defined it otherwise:
interface Foo<T extends Bar> {
T doSomething();
<T extends Bar> T doAnotherThing(Foo<T> foo);
}
Because such a way of defining doAnotherThing
introduces another generics parameter. What's confusing the interface's parameter and one from method share the name, i.e. T
.
(BTW, I was surprised to learn Java allows such confusing name clash)
The last definition could be replaced with:
interface Foo<T extends Bar> {
T doSomething();
<Y extends Bar> Y doAnotherThing(Foo<Y> foo);
}
which makes it more clear why public BarImpl doAnotherThing(Foo<BarImpl> foo)
is not a proper way of overriding of this method.
I believe my original answer is incorrect. The following seems to work fine though. Just make the doSomething method public and it compiles fine under Java6.
abstract class Bar {
// some neat stuff
}
class BarImpl extends Bar {
// some cool stuff
}
interface Foo<T extends Bar> {
public T doSomething();
}
class FooImpl implements Foo<BarImpl> {
public BarImpl doSomething() {
return null;
}
}
public class Test {
<T extends Bar> T doAnotherThing(Foo<T> foo) {
return foo.doSomething();
}
}
精彩评论