Как использовать универсальный класс в качестве параметра и расширить тип?

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