Вывод типа параметра класса Java Generics - PullRequest
4 голосов
/ 16 июня 2010

Учитывая интерфейс:

public interface BasedOnOther<T, U extends BasedList<T>> {

    public T getOther();

    public void staticStatisfied(final U list);

}

BasedOnOther<T, U extends BasedList<T>> выглядит очень некрасиво в моих случаях использования.Это связано с тем, что параметр типа T уже определен в части BasedList<T>, поэтому «уродство» возникает из-за того, что T нужно вводить дважды.

Задача :Можно ли позволить компилятору Java выводить общий тип T из BasedList<T> в определении общего класса / интерфейса?

В конечном счете, я хотел бы использовать такой интерфейс:

class X implements BasedOnOther<Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
} // Does not compile, due to invalid parameter count.

Где Y extends BasedList<SomeType>.

Вместо:

class X implements BasedOnOther<SomeType, Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
}

Где Y extends BasedList<SomeType>.

Обновление : ColinD предложил

public interface BasedOnOther<T> {
    public T getOther();
    public void staticSatisfied(BasedList<T> list);
}

Невозможно создать реализацию, такую ​​как:

public class X implements BasedOnOther<SomeType> {
    public SomeType getOther() { ... }
    public void staticStatisfied(MemoryModel list);
} // Does not compile, as it does not implement the interface.

Где MemoryModel extends BasedList<SomeType>, что необходимо (так как оно предоставляет другие методы).

Ответы [ 4 ]

3 голосов
/ 16 июня 2010

Выглядит так, как будто вам на самом деле не нужен параметр типа U extends BasedList<T>, если вам на самом деле не нужно ничего делать в классе, который требует какого-то определенного подкласса / реализации BasedList<T>. Интерфейс может быть просто:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<T> list);
}

Редактировать : Судя по твоему обновлению, я не думаю, что ты сможешь это сделать. Я думаю, вам придется либо просто пойти с вашей первоначальной декларацией, либо сделать какой-то промежуточный тип, который задает T, например:

public interface BasedOnSomeType<U extends BasedList<SomeType>>
         extends BasedOnOther<SomeType, U>
{
}

public class X implements BasedOnSomeType<MemoryModel> { ... }

Хотя это кажется пустой тратой, и я не думаю, что оригинальная декларация выглядит так плохо.

2 голосов
/ 26 июня 2013

Как насчет этого?

public interface BasedOnOther<T> {
    public T getOther();
    public <U extends BasedList<T>> void staticStatisfied(final U list);
}
0 голосов
/ 21 октября 2017

У меня недавно была очень похожая проблема.

Я хотел бы предложить, если вам не нужно специально ссылаться на MemoryModel, то есть, если U extends BasedList<T> достаточно, тогда я определенно буду делать то, что Пепеответил.

Однако, если вы должны проверить, по крайней мере, для двух методов, что оба метода должны использовать MemoryModel специально, а вывод типа в ответе Пепе недостаточен, тогда единственный способ использовать неуклюжий /Подробный параметризованный конструктор более прост в использовании обобщенного метода для определения параметров .Вам нужно будет создать общие статические фабричные методы для каждого конструктора, где фабричные методы делают вывод типа (конструкторы не могут выводить вывод в Java).

Как это сделать, описано в

Эффективная Java, Джошуа Блок;Пункт 27: Пользуйся общими методами

Я также объяснил это и процитировал решение (с кодом) здесь .

0 голосов
/ 10 декабря 2013

ColinD почти правильно.Вероятно, вы захотите вот что:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<? extends T> list);
}

Это потому, что аргументы метода ковариантны , но общие шаблоны инвариантны .Посмотрите на этот пример:

public test() {
  Number n1;
  Integer n2; //Integer extends Number.  Integer is a more-specific type of Number.

  n1 = n2; //no problem
  n2 = n1; //Type mismatch because the cast might fail.

  List<Number> l1;
  List<Integer> l2;
  List<? extends Number> l3;

  l1 = l3; //No problem.
  l1 = l2; //Type mismatch because the cast might fail.
}

Почему:

Попытка поместить Integer туда, где принадлежит Number, является ковариацией, и это обычно правильно для аргументов функции.

Попытка поместить Number туда, где принадлежит Integer, является противоположностью, противоположностью, и это обычно правильно для возвращаемых значений функции.Например, если вы определили функцию для возврата числа, она может вернуть целое число.Однако если вы определили его как возвращающее целое число, вы не сможете вернуть число, потому что это может быть, например, число с плавающей точкой.

Когда вы имеете дело с обобщениями, компилятор не может сказатьесли общий параметр (в вашем случае, T) будет ковариантным или контравариантным.Например, в вашем коде T является возвращаемым значением и частью аргумента.По этой причине общие параметры по умолчанию инвариантны.

Если вы хотите ковариации, используйте <? extends T>.Для контравариантности используйте <? super T>.Как правило, вы всегда хотите указать ковариацию / контравариантность для всех открытых функций.Для частных функций это не так важно, потому что вы, как правило, уже знаете типы.

Это не относится к Java, другие объектно-ориентированные языки имеют аналогичную проблему.

...