views:

61

answers:

5

Hey,

Imagine we have following classes:

public interface MyInterface<T> {
    List<T> getList(T t);
}

abstract class BaseClass<T extends Number> implements MyInterface<T> {
    @Override
    public List<T> getList(Number t) {
        return null;
    }
}

class ChildClass extends BaseClass<Integer> {
    @Override
    public List<Integer> getList(Integer t) {
        return super.getList(t);  //this doesn't compile
    }
}

getList in ChildClass doesn't compile, the output is:

abstract method getList(T) in com.mypackage.MyInterface cannot be accessed directly

I can't get why BaseClass.getList method isn't overriden in ChildClass.

But what makes me completely confused is the fix that makes it compile:

class ChildClass extends BaseClass<Integer> {
    @Override
    public List<Integer> getList(Integer t) {
        return super.getList((Number) t);  //Now it compiles!
    }
}

So I cast Integer to Number, and is solves the problem.

Could anyone explain what's going on in this code?

+2  A: 

It doesn't override because the abstract method takes a Number as a parameter and the concrete method takes an Integer. They must be the same in order to override.

You should change your abstract class implementation to take type T as a parameter.

Tyler
+3  A: 

Your base class should look like:

abstract class BaseClass<T extends Number> implements MyInterface<T> {
    @Override
    public List<T> getList(T t) {
        return null;
    }
}

You weren't using T, but the Number class as a parameter.

Marc
You're right, I should have used T. But still, why converting Integer to Number solves it?
denis_k
Because you do a call to getList(Integer t), which refers to the getList(T t) from the interface (which has no implementation). The getList(Number t) is an overloaded function, but that one is not the best match.
Marc
OK, this makes sense. Thanks!
denis_k
+2  A: 

Why isn't the superclass method defined as

public List<T> getList(T t)

?

Pointy
A: 

As other colleagues pointed out the reason for issue is incorrect signature of parent method. The reason why the casting works is due to the way how compiler treats generics. It guarantees that there won't be runtime ClassCastException issues if you use generic but only if you don't do casting. As soon as you did it you actually said compiler to shut up as you know better what your type really is. However after this you potentially could get ClassCastException in runtime (not in this case I assume)

iYasha
+1  A: 

What is going on in the imaginary class.

 abstract class BaseClass<T extends Number> implements MyInterface<T> {
    @Override
    public List<T> getList(Number t) {
        return null;
    }
}

This class has one generic parameter (T) that has to extend Number class and implement the interface MyInterface

You also Try to override a method that does not exists, because this class do not extend other any class. While a class is implementing an interface there is no need to override the interface method because the are only the description.

What happen if we remove the @override annotation.

 abstract class BaseClass<T extends Number> implements MyInterface<T> {

    public List<T> getList(Number t) {
        return null;
    }
}

In this case we do not implement the method from the interface but create a new one, a this method parameter is Number that is same type as T, it will probably cause some error that class has two the same methods. (not tested by compiler)

Them implementation of this method should look like this

 abstract class BaseClass<T extends Number> implements MyInterface<T> {

    public List<T> getList(T t) { //Because T is allready restricted to be Number
        return null;
    }
}

And when You specify the type You will not have a problem to call this method when you override it

class ChildClass extends BaseClass<Integer> {
    @Override
    public List<Integer> getList(Integer t) {
        return super.getList(t); 
    }
}

In advance You don have to implement it only for return null and then override it in some child class. What You can do is create class like this

 abstract class BaseClass<T extends Number> implements MyInterface<T> {

    private List<T> list = new ArrayList<T>(); //The way of initialization is up to You

    public List<T> getList() { //Because T is allready restricted to be Number
        return list;
    }

}
Vash