Я пытаюсь использовать дженерики в своем проекте, чтобы иметь базовый класс, который может использовать разные типы объектов. Но у меня возникают проблемы при попытке передать эти общие типизированные классы в качестве параметров в некоторых случаях.
Итак, я думаю, мой вопрос в том, есть ли способ использовать определенный универсальный типизированный интерфейс и передавать расширения универсального типа?
Извините, если я странно формулирую это, я не очень привык пытаться объяснять такие вопросы. Я был бы рад получить какое-то представление о дженериках, я чувствую, что не могу понять, как они на самом деле работают. Я прочитал много сообщений о них, но я не смог найти ни одного, описывающего мою проблему или действительно помогающего мне понять все это.
Я пробовал множество вариантов расширения общего типа 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
, разве компилятор не должен принимать его как SomeClass
, потому что он наследует те же свойства, что и один?
РЕДАКТИРОВАТЬ 2 Кажется, теперь я решил свою проблему. У меня есть тенденция решать свою проблему сразу после того, как я сделаю пост о ней.
Как было предложено в комментарии, речь шла скорее о проблемах, которые он вызвал в других частях программы. Я не хотел смешивать другие части моей программы, потому что ее структура и мое чрезмерное использование дженериков довольно глупы, и я не хотел делать ее еще более запутанной.
Когда я изменил coolMethod()
, чтобы он мог принимать любые NiceObject<SomeClass>
, появились другие проблемы. У меня были методы в NiceObject
, которые должны были использовать собственный тип NiceObject
в качестве параметров. Я решил эту проблему, создав метод, который возвращал бы себя, а подтипы реализовывали бы этот метод.
public interface NiceObjectUser <N extends NiceObjectUser<N>> {
public N getSelf();
}
Я также сделал другие глупости, чтобы ошибки исчезли, и мне пришлось реорганизовать довольно много других классов. в конце концов, хотя, это работает так что угодно.
Я ценю ответ, который объяснил некоторые вещи о дженериках, которые я теперь принял. Теперь я чувствую, что у меня есть более высокий уровень понимания дженериков.
Вы пробовали кастинг? coolMethod((NiceObject<EpicClass>) объект)
«Я просто получаю неприменимые параметры где-то в моей программе», что может просто означать, что вам нужно немного реорганизовать свой код, если вы начали успешно использовать «coolMethod» с некоторыми другими параметрами. Возможно, вы можете включить пример того, что больше не работает, и мы можем предложить решение или решение, чтобы решить все это.
Проблема здесь в том, что хотя 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>
. Это означает, что вы можете использовать Box<String>
везде, где ожидалось 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
, означает, что правила подтипа не работают так, как вы могли бы ожидать. В общем, предложенные вами альтернативы - это то, что нужно делать, когда вы сталкиваетесь с такой проблемой; чтобы выяснить, почему они не работают, нам нужно больше узнать о специфике того, что вы пытаетесь сделать.
Спасибо, что разобрали каждую часть вашего кода. Можете ли вы также предоставить блок кода полный, который мы можем просто скопировать/вставить и скомпилировать самостоятельно?