Java-дженерики называют clash, имеет такое же стирание

У меня есть суперкласс Foo. И класс Бар, расширяющий его.

public class Bar extends Foo

Функция в Foo:

protected void saveAll(Collection<?> many)

Функция в баре:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

Получение ошибки:

 Name clash: The method saveAll(Collection<MyClass>) of type Bar has the same erasure as saveAll(Collection<?>) of type Foo but does not override it.

Что я делаю неправильно?

6 ответов

Решение

Вы переопределяете saveAll метод с несовместимым типом. Возможно, вы хотите сделать что-то вроде:

public class Bar extends Foo<MyClass>

Функция в Foo<E>

protected void saveAll(Collection<E> many)

и функционировать в баре:

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

Из-за функции стирания типов в Java JVM не сможет узнать, является ли этот метод параметризованным типом MyClass или первым, который должен быть вызван.

Если возможно или применимо, наиболее часто используемый шаблон, который я видел, чтобы избежать этого, это изменение класса Foo иметь параметризованный тип:

public class Foo<T> {
    protected void saveAll(Collection<T> many) {}
}

а затем Бар просто реализовать Foo для вашего конкретного типа:

public class Bar extends Foo<MyClass> {
    public void saveAll(Collection<MyClass> many) {
        super.saveAll(many);
    }
}

Во время выполнения типы параметров заменяются на Object, Так saveAll(Collection<?>) а также saveAll(Collection<MyClass>) превращаются в saveAll(Collection), Это столкновение имен. Смотрите здесь для деталей.

Вы могли бы сделать это:

public class Foo<T> {
    protected void saveAll(Collection many) {
        // do stuff
    }
}

public class Bar extends Foo<MyClass> {
}

Когда компиляторы компилируются в байтовый код, происходит процесс, называемый Erasure. Это удаляет информацию о типе из коллекций. Я полагаю, что он будет делать приведения вручную и т.д. как часть процесса генерации байтового кода. Если вы удалите общие части вашего класса (т.е. <..>), то увидите, что у вас есть два метода saveAll. Ошибка в том, что у вас есть два метода сохранения, все будут иметь одинаковую подпись. Коллекции имеют тип объекта в байт-коде.

Попробуйте удалить <..>, что может сделать его более понятным. Когда вы вставляете <...> обратно, учитывайте название методов. Если они разные, то должны скомпилироваться.

Также я не думаю, что это проблема гибернации, поэтому этот тег следует удалить. Это общая проблема Java у вас есть.

Что вы можете сделать здесь, это ввести класс

public class Bar extends Foo<MyClass>

а затем есть типы методов для T

public void saveAll(Collection<MyClass> stuff) {
   super.saveAll(stuff);
}

и тогда объявление Foo будет что-то вроде

public abstract class Bar extends Foo<T> {
    public void saveAll(Collection<T> stuff) {
}

Вы просто переопределяете методы с разными сигнатурами.

Хорошей идеей будет использование правила PECS (производитель - расширяет, потребитель - супер), описанного во второй редакции Effective Java Джошуа Блоха.

согласно этому правилу это должно выглядеть так.

В классе Foo:

public class Foo<E>{

protected void saveAll(Collection<? super E> many){....}

protected void getAll(Collection<? extends E> many){....}

}

Хотя существующие ответы верны, эта ошибка легко возникает, когда вы объявляете фактический тип не в "extends... ", но в фактическом имени класса:

Неправильно:

public class Bar<MyClass> extends Foo
{
    ...
}

Правильный:

public class Bar extends Foo<MyClass>
{
    ...
}
Другие вопросы по тегам