Reputation: 699
Does anyone know the definitive answer to why the following isn't allowed by the java compiler?
class BaseClass {
public <T extends Number> T getNumber(){
return null;
}
}
class SubClass extends BaseClass{
@Override
public <T extends Integer> T getNumber(){
return null;
}
}
this causes the compiler to complain with:
"The method getNumber() of type SubClass must override a superclass method"
Now, when I put this to my colleagues some have tried to explain that it will cause confusion with the compiler. However, as was also pointed out the following, which is conceptually similar, is compilable.
class BaseClass<T extends Number> {
public T getNumber(){
return null;
}
}
class SubClass<T extends Integer> extends BaseClass<T>{
@Override
public T getNumber(){
return null;
}
}
This can be abused if the subclass calls the super implementation, but the compiler provides a warning to this effect. My only conclusion is that this is a compiler oversight on the part of folks at Sun (can't bring myself to say Oracle :-S).
Anyone have the definitive answer to this one?
Upvotes: 2
Views: 762
Reputation: 66753
Suppose it would indeed be allowed to add more restrictions to the type parameter on a derived class.
Then what if your class also had a <T extends Number> void setNumber(T number)
method?
BaseClass foo = new SubClass();
long longValue = 42;
foo.<Long>setNumber(longValue);
The above would be accepted by the compiler because BaseClass.setNumber accepts any type parameter derived from Number
. But the actual instance only accepts integers!
You could argue that if the type parameter is only used for the return value, it should automatically be considered covariant. But then the compiler would have to make sure that you don't use the type parameter inside the method body in a non-covariant way.
(In C# 4.0 this was actually solved, but it involves explicitly marking your type parameters as covariant or contravariant with the out
and in
keywords. It was not something that could simply be allowed by the compiler without changing the language.)
Upvotes: 5
Reputation: 89179
I don't quite follow your issue:
When I implemented your first portion of source code and compiled using javac *.java
, it compiled fine. However, if I changed your SubClass.getNumber()
method to do this...
/* (non-Javadoc)
* @see constraint.base.BaseClass#getNumber()
*/
@Override
public <T extends Number> T getNumber() {
// TODO Auto-generated method stub
return super.getNumber();
}
I get the following error:
SubClass.java:18: type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Number
return super.getNumber();
^
1 error
The reason for the code above is that by calling super.getNumber()
, the compiler doesn't have any input that infer type T
hence it cannot determine what T
will be returned by super.getNumber()
and, thus it can't satisfy the return type with SubClass.getNumber()
Now, in order to have obtained the error from the compiler, "The method getNumber() of type SubClass must override a superclass method"
, your BaseClass
should have been abstract along with it's getNumber()
method.
As for the 2nd piece of code you've provided, the subclass can, in effect, call super.getNumber()
as the compiler will know (on compiling) that the SubClass's Generic Parameter Type is inferred to the Parameter Type of BaseClass
.
Besides that, I really didn't know what you were trying to ask.
Upvotes: 0
Reputation: 421090
@Wim Coenen provided a good answer.
Here is a small clarification.
If this would compile, what would you expect happened:
import java.math.BigInteger;
class BaseClass {
public <T extends Number> T getNumber(T n) {
System.out.println("Int value: " + n.intValue());
return n;
}
}
class SubClass extends BaseClass {
@Override
public <T extends BigInteger> T getNumber(T n) {
System.out.println("Int value: " + n.intValue());
// BigInteger specific!
System.out.println("Bit count: " + n.bitCount());
return n;
}
}
public class Main {
public static void main(String[] args) {
BaseClass sub = new SubClass();
// sub looks like a BaseClass...
// ...but since it's a SubClass it expects a BigInteger!
sub.getNumber(new Integer(5));
}
}
Upvotes: 0
Reputation: 310980
Because there is no covariance here. Covariance means 'varies with', and it refers to the return type varying as the containing class type. That's not happening here. There is no 'co' in your covariance.
Upvotes: 0