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

Java Generics: общий тип, определенный только как возвращаемый тип

Я смотрю на какой-то код GXT для GWT, и я наткнулся на это использование Generics, что я не могу найти другого примера в учебниках Java. Имя класса com.extjs.gxt.ui.client.data.BaseModelData, если вы хотите посмотреть на весь код. Вот важные части:

private RpcMap map;

public <X> X get(String property) {
  if (allowNestedValues && NestedModelUtil.isNestedProperty(property)) {
    return (X)NestedModelUtil.getNestedValue(this, property);
  }
  return map == null ? null : (X) map.get(property);
}

X больше нигде не определяется в классе или где-либо в иерархии, и когда я нажимаю "перейти к объявлению" в eclipse, он просто переходит к <X> в общедоступной сигнатуре метода.

Я попытался вызвать этот метод со следующими двумя примерами, чтобы увидеть, что происходит:

public Date getExpiredate() {
    return  get("expiredate");
}

public String getSubject() {
    return  get("subject");
}

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

Означает ли это, что Generics допускает магическое возвращаемое значение, которое может быть чем угодно и просто взорвется во время выполнения? Это похоже на то, что должны делать дженерики. Может ли кто-нибудь объяснить это мне и, возможно, дать мне ссылку на какую-то документацию, которая объясняет это немного лучше? Я прошел через Sun 23 page pdf на generics, и каждый пример возвращаемого значения определяется либо на уровне класса, либо находится в одном из переданных параметров.

4b9b3361

Ответ 1

Метод возвращает тип того, что вы ожидаете от него (<X> определяется в методе и абсолютно неограничен).

Это очень, очень опасно, поскольку не предусмотрено, что тип возврата действительно соответствует возвращенному значению.

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

Я бы сказал: используйте такие конструкции с осторожностью, потому что вы теряете почти все виды безопасности и получаете только то, что вам не нужно писать явное при каждом обращении к get().

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

Ответ 2

Тип объявляется в методе. То, что означает "<X>". Тип привязывается только к методу и относится к определенному вызову. Причина, по которой ваш тестовый код компилируется, заключается в том, что компилятор пытается определить тип и будет жаловаться только в том случае, если он не может. Есть случаи, когда вы должны быть явными.

Например, декларация для Collections.emptySet() -

public static final <T> Set<T> emptySet()

В этом случае компилятор может угадать:

Set<String> s = Collections.emptySet();

Но если это невозможно, вы должны ввести:

Collections.<String>emptySet();

Ответ 3

Я просто пытался выяснить то же самое с классом GXT. В частности, я пытался вызвать метод с сигнатурой:

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

Чтобы вызвать вышеупомянутый метод из вашего кода и заставить его передать X в String, я делаю следующее:

public String myMethod(Data data) {
    Model model = new Model(data);
    return model.<String>get("status");
}

Вышеприведенный код вызовет метод get и скажет ему, что возвращаемый им тип X должен быть возвращен как строка.

В том случае, когда метод находится в том же классе, что и вы, я обнаружил, что должен называть его "этим". Например:

this.<String>get("status");

Как говорили другие, это довольно неряшливо и опасно для команды GXT.

Ответ 4

Интересно отметить, что RpcMap (API GXT 1.2)

получить заголовок:

public java.lang.Object get(java.lang.Object key)

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

Ответ 5

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

public String getExpireDate() {
  return  get("expiredate");
}

Ответ 6

Да, это опасно. Обычно вы защищаете этот код так:

<X> getProperty(String name, Class<X> clazz) {
   X foo = (X) whatever(name);
   assert clazz.isAssignableFrom(foo);
   return foo;
}

String getString(String name) {
  return getProperty(name, String.class);
}

int getInt(String name) {
  return getProperty(name, Integer.class);
}