Java Generics: универсальный тип, определенный только как возвращаемый тип - PullRequest
66 голосов
/ 04 декабря 2008

Я смотрю на некоторый код 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 не определено нигде в классе или где-либо в иерархии, и когда я нажимаю «перейти к объявлению» в затмении, он просто переходит на <X> в сигнатуре открытого метода.

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

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

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

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

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

Ответы [ 6 ]

53 голосов
/ 04 декабря 2008

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

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

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

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

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

39 голосов
/ 04 декабря 2008

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

Например, объявление для Collections.emptySet() равно

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

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

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

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

Collections.<String>emptySet();
5 голосов
/ 27 сентября 2011

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

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

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

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

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

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

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

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

2 голосов
/ 04 декабря 2008

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

public String getExpireDate() {
  return  get("expiredate");
}
2 голосов
/ 04 декабря 2008

Интересная заметка от RpcMap (GXT API 1.2)

Заголовок get:

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

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

0 голосов
/ 21 февраля 2009

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

<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);
}
...