Java: путаница наследования дженериков - PullRequest
3 голосов
/ 15 октября 2010

Представьте, что у нас есть следующие классы:

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 в ChildClass не компилируется, вывод:

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

Я не могу понять, почему *Метод 1009 * не переопределяется в ChildClass.

Но что меня смущает, так это исправление, которое делает его компиляцией:

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

Итак, я приведу Integer к Number, и это решает проблему.

Может кто-нибудь объяснить, что происходит в этом коде?

Ответы [ 5 ]

5 голосов
/ 15 октября 2010

Ваш базовый класс должен выглядеть так:

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

Вы использовали не T, а класс Number в качестве параметра.

2 голосов
/ 15 октября 2010

Почему метод суперкласса не определен как

public List<T> getList(T t)

?

2 голосов
/ 15 октября 2010

Это не переопределяет, потому что абстрактный метод принимает число в качестве параметра, а конкретный метод принимает целое число.Для того чтобы переопределить, они должны быть одинаковыми.

Вам следует изменить реализацию абстрактного класса, чтобы в качестве параметра принимать тип T.

1 голос
/ 15 октября 2010

Что происходит в воображаемом классе.

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

Этот класс имеет один общий параметр (T), который должен расширять класс Number и реализовывать интерфейс MyInterface

Вы также пытаетесь переопределить метод, который не существует, потому что этот класс не расширяет другие классы. В то время как класс реализует интерфейс, нет необходимости переопределять метод интерфейса, потому что это только описание.

Что произойдет, если мы удалим аннотацию @override.

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

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

В этом случае мы не реализуем метод из интерфейса, но создаем новый. Параметр этого метода имеет значение Number того же типа, что и T, что, вероятно, приведет к некоторой ошибке, связанной с те же методы. (не проверено компилятором)

Их реализация этого метода должна выглядеть следующим образом

 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;
    }
}

А когда вы указываете тип, у вас не возникнет проблем с вызовом этого метода при его переопределении

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

Заранее Вам не нужно реализовывать это только для возврата null, а затем переопределить его в каком-нибудь дочернем классе. Что вы можете сделать, так это создать класс

 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;
    }

}
0 голосов
/ 15 октября 2010

Как отметили другие коллеги, причиной проблемы является неверная подпись родительского метода.Причина того, что приведение работает, заключается в том, как компилятор обрабатывает дженерики.Это гарантирует, что не возникнет проблем ClassCastException во время выполнения, если вы используете универсальный, но только если вы не выполняете приведение.Как только вы это сделали, вы фактически сказали компилятору замолчать, поскольку вы лучше знаете, какой у вас тип.Однако после этого вы потенциально можете получить ClassCastException во время выполнения (не в этом случае, я полагаю)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...