Как использовать общий класс в качестве параметра и расширить тип?

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

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

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

Я пробовал множество вариантов расширения общего типа 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();
}

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

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

Спасибо, что разобрали каждую часть вашего кода. Можете ли вы также предоставить блок кода полный, который мы можем просто скопировать/вставить и скомпилировать самостоятельно?

Code-Apprentice 10.04.2019 19:05

Вы пробовали кастинг? coolMethod((NiceObject<EpicClass>) объект)

Bakon Jarser 10.04.2019 19:08

«Я просто получаю неприменимые параметры где-то в моей программе», что может просто означать, что вам нужно немного реорганизовать свой код, если вы начали успешно использовать «coolMethod» с некоторыми другими параметрами. Возможно, вы можете включить пример того, что больше не работает, и мы можем предложить решение или решение, чтобы решить все это.

Valentin Ruano 10.04.2019 19:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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>. Это означает, что вы можете использовать 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, означает, что правила подтипа не работают так, как вы могли бы ожидать. В общем, предложенные вами альтернативы - это то, что нужно делать, когда вы сталкиваетесь с такой проблемой; чтобы выяснить, почему они не работают, нам нужно больше узнать о специфике того, что вы пытаетесь сделать.

Другие вопросы по теме