Можно ли избежать отмеченных предупреждений при переопределении метода с параметрами сырого типа?

Я расширяю класс, определенный в библиотеке, которую я не могу изменить:

public class Parent
{
    public void init(Map properties) { ... }
}

Если я определяю класс "Ребенок", который расширяет Parent, и я использую Java 6 с generics, как лучше всего переопределить метод init без получения предупреждений без предупреждения?

public class Child extends Parent
{
    // warning: Map is a raw type. References to generic type Map<K,V> should be parameterized
    public void init(Map properties) { }
}

Если я добавлю общие параметры, я получаю:

   // error: The method init(Map<Object,Object>) of type Child has the same erasure as init(Map) of type Parent but does not override it
   public void init(Map<Object,Object>) { ... }
   // same error
   public void init(Map<? extends Object,? extends Object>) { ... }
   // same error
   public void init(Map<?,?>) { ... }

Эта ошибка возникает независимо от того, используется ли я конкретный тип, ограниченный шаблон или неограниченный шаблон. Есть ли правильный или идиоматический способ переопределить неосновный метод без предупреждений и без использования @SuppressWarnings ( "unchecked" )?

Ответы

Ответ 1

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

Я считаю, что лучше всего добавить аннотацию @SuppressWarnings("unchecked") к параметру raw-type, а не к методу, поэтому вы не будете подавлять другие предупреждения генераторов, которые могут иметься в вашем собственном коде.

Ответ 2

Короткий ответ: нет способа сделать это.

Неудовлетворительный ответ: отключите (конкретные) предупреждения в IDE/build.xml.

Если вы не можете изменить библиотеку, увы, вы должны придерживаться не общих методов.

Проблема заключается в том, что, несмотря на стирание типа, как init() имеют одну и ту же подпись, они могут быть разными способами - или одинаковыми (*). Компилятор не может сказать, должен ли он переопределять или перегружать, поэтому он запрещен.

(*) Предположим, что разработчик библиотеки означал init (Map < String, Integer > ). Теперь вы выполняете init (Map < String, String > ). Это перегрузка, и в vtable класса Child должны существовать два метода.

Но что, если разработчик библиотеки имел в виду init (Map < String, String > )? Затем он переопределяет, и ваш метод должен заменить оригинальный init в классе Child, и в vtable Child будет только один метод.

P.S. Я ненавижу, как Generics реализованы в Java: - (

Ответ 3

Я думаю, что выше ответ должен был сказать @SuppressWarnings ( "rawtypes" ) вместо этого.

Ответ 4

Вы должны объявить метод с той же сигнатурой, что и родительский, и поэтому при компиляции вы получите предупреждения. Вы можете подавить их с помощью @SuppressWarnings ( "unchecked" )

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