Подтвердить что ты не робот

Почему в компиляторе нет уникального максимального экземпляра?

У меня есть следующие классы:

public class Obj<T> extends BaseModel {

    public static final String OBJECT = "object";

    public Obj(T object) {
        setObject(object);
    }

    public T getObject() {
        return get(OBJECT);
    }

    public void setObject(T object) {
        set(OBJECT, object);
    }
}

А...

/** This is a 3rd party library class **/
public class BaseModel implements ModelData, Serializable {
  //...members and stuff...

  @SuppressWarnings({"unchecked", "rawtypes"})
  public <X> X get(String property) {
    X obj = null;
    if (start > -1 && end > -1) {
      Object o = map.get(property.substring(0, start));
      String p = property.substring(start + 1, end);
      if (o instanceof Object[]) {
        obj = (X) ((Object[]) o)[Integer.valueOf(p)];
      } else if (o instanceof List) {
        obj = (X) ((List) o).get(Integer.valueOf(p));
      } else if (o instanceof Map) {
        obj = (X) ((Map) o).get(p);
      }
    } else {
      obj = (X) map.get(property);
    }
    return obj;
  }
}

При компиляции я получаю следующую ошибку.

type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()

Это не происходит в Eclipse, который, насколько я могу судить, использует тот же JDK, что и моя сборка Ant. Я видел поток SO о проблеме компилятора Sun, но это, казалось, было для статических методов, объявляющих типы "на лету".

Почему я получаю эту ошибку, и что еще более важно, как мне ее обойти?

До сих пор единственное, что я нашел, - это включить мой метод следующим образом:

@SuppressWarnings({"unchecked"})
public T getObject() {
    return (T) get(OBJECT); //yuck
}

Рассказывая, что я нахожусь на трещине, и это правильный способ.

4b9b3361

Ответ 1

Он не компилируется, потому что ваш код ожидает слишком многого от генериков → то есть, <X> X part in:

public <X> X get(String property) { ... }

В следующем коде:

public T getObject() {
  return get(OBJECT);
}

вы должны иметь в виду, что генерические файлы всегда "разворачиваются" до, компилятор фактически начинает компилировать код Java. Это шаг предварительной обработки.

В вашем случае компилятор не знает, что использовать для замены X во время компиляции. Компилятор должен быть уверен в типе X, потому что ему нужно проверить его против T, чтобы проверить код. Отсюда ошибка...

Решение вашей проблемы заключается в замене <X> X с объектом:

public Object get(String property) { ... }

и добавьте бросок в:

public T getObject() {
  return (T) get(OBJECT);
}

Во время компиляции вы получите предупреждение без отметки, но ваш код будет скомпилирован (так что да, ваше обходное решение действительно).

Ответ 2

Это фиктивный bug, который был исправлен в Java SE 7.

Ответ 3

Параметры типа метода чаще всего неявно выводятся из аргументов этого метода. Однако обратите внимание, что get не имеет явной связи между аргументом и параметром типа:

public <X> X get(String property)

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

public T getObject() {
    return super.<T>get(OBJECT);
}

Вы также можете просто быть прямым и использовать <Object>, но вам все равно придется использовать этот непроверенный бросок, чтобы вернуть его в T. Обратите внимание, что явный аргумент нуждается в квалификаторе, обычно в имени экземпляра класса. Так как ваш пример использовал метод суперкласса, его ссылка неявна через super.

Это не решает основной проблемы применения общего метода (<X> X get) внутри не-общего класса (BaseModel). Обратите внимание, что код в библиотеке приводит к принудительному типу приведения к аргументу типа. Этот стиль действительно является одним из решений для резервного копирования общих функций в не-общий код Java. Похоже, что они пытаются скрыть это от пользователей библиотеки, но поскольку они не генерировали класс, тип не может быть выведен из экземпляра (т.е. Вы действительно хотите иметь Obj<T> extends BaseModel<T>).

[EDIT: исправлено и объяснено аргумент типа метода]

Ответ 4

Я столкнулся с аналогичной проблемой с проектом, используя Apache Pivot. Клиентский код был пронизан такими строками, как:

boolean foo = org.apache.pivot.json.JSON.get(item, "foo");

Код будет скомпилирован в Eclipse, но не будет использовать Maven или javac из командной строки. Похоже, что Ошибка 6302954, но я все еще вижу его после обновления до последнего JDK.

Поскольку класс JSON предоставляется Pivot, это не то, что я мог бы изменить в своем собственном исходном дереве (форматирование библиотеки не является вариантом в этом проекте)

Решение, которое сработало для меня, было получено из первого ответа в отчете об ошибке, изменив код для чтения:

boolean foo = org.apache.pivot.json.JSON.<Boolean>get(item, "foo");