Наследование универсального интерфейса в java

#java #inheritance

#java #наследование

Вопрос:

Я создал интерфейс на Java с помощью следующего метода:

 interface ReaderInterface{
      public <k,v> Map<k,v> fetch(k id);
      public <k,v> Map<k,v> fetchAll(); 
}
 

Затем я создал класс, который реализует этот интерфейс:

 class JSONreaderImpl implements ReaderInterface{
    public Map<String, String> fetchAll(){ 
        // This compiler is allowing to be override from interface 
    }

    public Map<String, String> fetch(String id){ 
        // This is not being considered as an override method, compiler throwing an error to implement the fetch method 
    }

}
 

Мой вопрос здесь в том, почему метод fetchAll рассматривается как метод переопределения при его создании в определенном классе и почему не метод выборки.

Не могли бы вы, пожалуйста, помочь мне понять это?

Комментарии:

1. Извините, это недопустимая Java, я проголосовал против и проголосовал за закрытие. Ваши фрагменты должны быть скомпилированы. Interface , Class , <k,v> Map<k,v> не работают

2. @AndrewTobilko Это простая ошибка, я не понимаю, почему вы отменили мою правку.

3. @AndrewTobilko также <k,v> Map<k,v> будет работать нормально. Использование маленьких букв немного неортодоксально, но код допустим.

4. Вы имели interface ReaderInterface<k,v> { Map<k,v> fetch(k id); etc } в виду «вместо этого»? Использование подписи <k,v> Map<k,v> fetch(k id) здесь не имеет для меня особого смысла.

5. @talex Я согласен с последним пунктом — это проблема дизайна (а не синтаксическая). Однако вы исправили пять опечаток, что много и указывает на то, что OP даже не пытался его скомпилировать. Для меня эта небрежность — небольшой признак неуважения к сообществу.

Ответ №1:

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

 interface ReaderInterface<k, v> {
    public Map<k, v> fetch(k id);
    public Map<k, v> fetchAll();
}

class JSONreaderImpl implements ReaderInterface<String, String> {
    public Map<String, String> fetchAll() {  return null; }

    public Map<String, String> fetch(String id) {  return null;  }
}
 

В противном случае (параметризуя каждый метод отдельно, вы должны разрешить такие реализации, как

     public Map<String, Boolean> fetchAll() {  return null; }
    public Map<Integer, List<Double>> fetch(Integer id) {  return null;  }
 

Комментарии:

1. Вопрос не в этом. Речь идет об универсальных методах, а не об универсальном классе.

2. @talex вы правы. Объясняется в отдельном ответе. Спасибо, что вернули разговор к сути.

Ответ №2:

Чтобы понять, почему fetch это не работает, вы можете посмотреть следующий пример.

 ReaderInterface foo = new JSONreaderImpl();
foo.<Integer, Integer>fetch(Integer.valueOf(1));
 

Это вызов fetch с <Integer, Integer> общими параметрами, но реализация ожидает, что они будут <String, String> . Чтобы предотвратить это, компилятор выдает ошибку.

Я не понимаю, почему fetchAll это работает. Вероятно, какая-то магия стирания, и на самом деле это предупреждение о «непроверенном переопределении».

Комментарии:

1. Вот почему у меня возник этот вопрос, почему поведение такое?

2. Потому что в противном случае компилятор не может обеспечить безопасность типов.

Ответ №3:

@talex прав, и на первоначальный вопрос определенно стоит ответить, несмотря на провокационный дизайн.

Удаление типа для общих методов объясняет, что параметр неограниченного типа компилируется Object . Итак, объявляя этот интерфейс

 interface Q {
  public <K, V> Map<K, V> fetch(K id);
}
 

у вас есть следующий интерфейс, который нужно расширять во время выполнения

 interface Q {
  public Map<Object, Object> fetch(Object id);
}
 

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

Таким образом, вы можете сделать так, чтобы следующая реализация была обнаружена как одна и та же сигнатура

 abstract class QA implements Q {
  public abstract Map<Integer, String> fetch(Object id);
}
 

но не может иметь этого в этой роли:

 abstract class QA implements Q {
  public abstract Map<Integer, String> fetch(Integer id);
}
 

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

 interface O {
    <K extends List, V> V process(K id);
}

abstract  class OA implements O {
    public abstract String process(List id);
}
 

Ответ №4:

вопросы не совсем понятны, но если это то, чего вы пытаетесь достичь

 interface Reader<T>.
{ 
  Map fetchAll(); 
  Map fetch(T id); 
}
 

Реализация

 public class JSONReader implements Reader<String> 
{ 
  @Override
  Map<String, String> fetch(String id)
  { 
   // code
  }
  @Override 
  Map<String, String> fetchAll()
  { 
    //code
  }  
} 
 

Комментарии:

1. если что-то в вопросе вам не совсем понятно, пожалуйста, не отвечайте. Действительно, лучше уточнить это в комментариях