#java #generics
#java #дженерики
Вопрос:
Я пытаюсь использовать generics в своем проекте, чтобы иметь базовый класс, который может использовать различные типы объектов. Но у меня возникают проблемы при попытке передать эти общие типизированные классы в качестве параметров в некоторых случаях.
Итак, я предполагаю, что мой вопрос в том, есть ли какой-либо способ использовать определенный универсальный типизированный интерфейс и передавать расширения универсального типа?
Извините, если я формулирую это странно, я не очень привык объяснять подобные вопросы. Я был бы рад получить какое-то представление о дженериках, я чувствую, что не могу понять, как они действительно работали. Я прочитал много сообщений о них, но не смог найти ни одного, которое описывало бы мою проблему или действительно помогло бы мне понять все это.
Я перепробовал множество вариантов расширения универсального типа T, также пытаясь использовать подстановочные знаки. Но я всегда просто получаю доступ ко многим вложенным обобщениям и захватам подстановочных знаков, и я просто создаю беспорядок.
У меня есть этот интерфейс:
public interface NiceObject<T extends SomeClass> {}
И затем я хочу иметь возможность расширять его с помощью разных реализаций:
public class EpicClass extends SomeClass {}
public class CoolObject implements NiceObject<EpicClass> {}
Для меня это работает нормально, но когда я пытаюсь передать реализацию NiceObject
в качестве параметра типа NiceObject<T>
, где T является расширением SomeClass
, он сообщает мне, что тип неприменим.
public void coolMethod(NiceObject<SomeClass> obj);
CoolObject obj = new CoolObject();
coolMethod(obj);
// this does not work
Я также пытался изменить метод некоторыми способами:
public void coolMethod(NiceObject<?> obj);
public void coolMethod(NiceObject<? extends SomeClass> obj);
public <S extends SomeClass> void coolMethod(NiceObject<S> obj);
// None of these produce my desired result
// I just end up with non-applicable parameters somewhere in my program.
Редактировать
Я постараюсь сделать свой вопрос немного понятнее:
NiceObject<EpicClass> obj = new CoolObject();
coolMethod(obj);
// Message:
// The method coolMethod(NiceObject<SomeClass>) in the type NiceMethods
// is not applicable for the arguments (NiceObject<EpicClass>)
Что меня смущает, так это то, что EpicClass
это явно расширение SomeClass
, разве компилятор не должен принимать его как a SomeClass
, потому что он наследует те же свойства, что и one ?
РЕДАКТИРОВАТЬ 2 Похоже, что теперь я решил свою проблему. У меня есть тенденция решать мою проблему сразу после того, как я сделаю сообщение об этом.
Как было предложено в комментарии, это было больше о проблемах, которые это вызвало в других частях программы. Я не хотел смешивать другие части моей программы, потому что ее структура и мое чрезмерное использование дженериков довольно глупы, и я не хотел делать это еще более запутанным.
Когда я изменил coolMethod()
, чтобы иметь возможность принимать любые NiceObject<SomeClass>
другие проблемы. У меня были методы в a NiceObject
, которые должны были использовать свой собственный тип NiceObject
в качестве параметров. Я решил это, создав метод, который возвращал бы сам себя, а подтипы реализовывали бы этот метод.
public interface NiceObjectUser <N extends NiceObjectUser<N>> {
public N getSelf();
}
Я также делал другие глупости, чтобы ошибки исчезли, и мне пришлось реорганизовать немало других классов. в конце концов, это работает так, как угодно.
Я действительно ценю ответ, который объясняет некоторые вещи о дженериках, которые я теперь принял. Теперь я чувствую, что у меня более высокий уровень понимания дженериков.
Комментарии:
1. Спасибо за разбиение каждого фрагмента вашего кода. Можете ли вы также предоставить полный блок кода, который мы можем просто скопировать / вставить и скомпилировать сами?
2. Вы пробовали кастинг? coolMethod((NiceObject<EpicClass>) obj)
3. «Я просто получаю неприменимые параметры где-то в моей программе», это может означать, что вам нужно немного реорганизовать свой код, если вы начали успешно использовать «coolMethod» с некоторыми другими параметрами. Возможно, вы можете включить пример того, что больше не работает, и мы можем предложить решение или решение для решения всего этого.
Ответ №1:
Проблема здесь в том, что, хотя EpicClass
это подтип SomeClass
, NiceObject<EpicClass>
это подтип NiceObject<SomeClass>
. Чтобы понять, почему, рассмотрим:
class Box<T> {
private T value;
T get() {
return value;
}
void set(T value) {
this.value = value;
}
}
Теперь подумайте о двух классах, таких как Object
и String
, где String
является подтипом Object
. Допустим, нам разрешено Box<String>
быть подтипом Box<Object>
. Это означало бы, что вы можете использовать a Box<String>
везде, где ожидалось a Box<Object>
. Теперь подумайте о таком коде:
Box<String> stringBox = new Box<String>();
Box<Object> objectBox = stringBox;
objectBox.set(new NonStringObject());
String string = stringBox.get(); // Error! Got a NonStringObject() from a string box!
В общем, тот факт, что поле может использовать a T
или создавать a T
, означает, что правила подтипов работают не так, как вы могли бы ожидать. В общем, предлагаемые альтернативы, которые вы опубликовали, — это путь, который нужно использовать, когда вы сталкиваетесь с подобной проблемой; чтобы выяснить, почему они не работают, нам нужно больше знать о специфике того, что вы пытаетесь сделать.