Почему компилятор утверждает, что уникального максимального экземпляра не существует? - PullRequest
26 голосов
/ 14 апреля 2011

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

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
}

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

Ответы [ 4 ]

21 голосов
/ 26 марта 2013

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

17 голосов
/ 15 апреля 2011

Он не компилируется, потому что ваш код ожидает слишком много от обобщений -> то есть, часть X в:

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

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

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

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

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

Решение вашей проблемы - заменить X на Object:

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

и добавить приведение в:

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

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

4 голосов
/ 15 апреля 2011

Параметры типа метода чаще всего неявно выводятся из аргументов этого метода.Однако обратите внимание, что 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>).

[ПРАВКА: исправлен и объяснен явный аргумент типа метода]

1 голос
/ 24 сентября 2012

Я только что столкнулся с подобной проблемой в проекте, использующем 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");
...