#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. если что-то в вопросе вам не совсем понятно, пожалуйста, не отвечайте. Действительно, лучше уточнить это в комментариях