tags:

views:

421

answers:

4

I have an interface

interface x {
    A getValue();
}

and the implementation

class y implements x {
    public B getValue() { return new B();}
}

B is a subclass of A. This works because of covariant overriding, I guess.

But if I rewrite the interface as

interface x{
    <T extends A> T getValue();
}

I get a warning in the implementation that

Warning needs a unchecked cast to conform to A.getValue()

What is the difference between 2 versions of the interface? I was thinking they are the same.

+3  A: 

Basically when you are doing a <T extends A> you are saying that you want a specific subclass of A, not any A, because then you could just do:

 A getValue();

The compiler is warning you that it can't ensure that a specific defined subclass of A is returned, only A itself.

EDIT: To try to explain this better, B is a subclass of A, but doesn't need to be the only subclass of A. Consider the following hierarchy.

B extends A C extends A but not B

Now I have a method:

public void someMethod(x value) {
    C c = x.getValue();
}

Following the generics, that should compile and guarantee no runtime class cast exception (there may be other issues with interfaces here but there are certainly cases that could be used to demonstrate this properly - I'm keeping with your example).

But what if you pass in an instance of y to this method?

Well, now I'm getting an B instead of a C. The compiler is warning you this could happen.

Yishai
B is a subclass of A , not sure what you mean by specific subclass
Surya
If `class C` was a subclass of `A`, then I could write `anX.<C>getValue()`, which is clearly wrong for `y`.
Tom Hawtin - tackline
(BTW: poor choice of type names.)
Tom Hawtin - tackline
+4  A: 

The second version of the interface is wrong. It says that the getValue will return any subclass you ask for - the return type will be inferred based on the expression on your left hand.

So, if you obtain a reference to an x (let's call it obj), you can legally make the following call without compiler warnings:

x obj = ...;
B b = obj.getValue();

Which is probably incorrect in your example, because if you add another class C that also extends A, you can also legally make the call:

C c = obj.getValue();

This is because T is not a type variable belonging to the interface, only to the method itself.

waxwing
Ahh ..so T is being inferred from the left hand side . Makes sense . So in a language that doesnt infer type from assignment like C# , what happens ?
Surya
A: 

So, to build on the two answers from AlbertoPL and Yishai, try this:

class y implements x {
    A getValue(){ return new B();}
}

B extends A, so that's fine. The caller doesn't need to know what subclass of A is returned, only that it extends A. So there we go :)

Cosmic Flame
The original code did actually work (with public).
Tom Hawtin - tackline
+1  A: 

What the compiler is telling you is that there is no way to resolve the generic. T is not defined anywhere except as output - which T are you talking about? If the interface was generified to be a type T:

interface x <T extends A> {
    T getValue()
}

then this would work, or if the method took a value that would define what type of return you wanted:

interface x {
    <T extends A> T getValue(T someArgument)
}

or

interface x {
    <T extends A> T getValue(Class<T> someArgument)
}

Then there would be no warning.

edit: See waxwing's post for an explanation as to why you should NOT be using generics for this particular problem. I am simply showing how generics would be appropriate.

aperkins