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
определяется нигде в классе или где-либо в иерархии, и когда я нажимаю "перейти к объявлению" в затмении, он просто переходит к <X>
в публичном методе подписи.
Я попытался вызвать этот метод с помощью следующих двух примеров, чтобы увидеть, что происходит:
public Date getExpiredate() {
return get("expiredate");
}
public String getSubject() {
return get("subject");
}
Они компилируются и не показывают ошибок или предупреждений. Я бы подумал, что, по крайней мере, мне придется сыграть роль, чтобы заставить это работать.
Означает ли это, что Generics допускает магическое возвращаемое значение, которое может быть чем угодно и просто взорвется во время выполнения? Это противоречит тому, что должны делать дженерики. Может кто-нибудь объяснить мне это и, возможно, дать мне ссылку на документацию, которая объясняет это немного лучше? Я просмотрел 23-страничный PDF-файл Sun, посвященный шаблонам, и каждый пример возвращаемого значения определяется либо на уровне класса, либо в одном из переданных параметров.
6 ответов
Метод возвращает тип того, что вы ожидаете (<X>
определяется в методе и является абсолютно неограниченным).
Это очень и очень опасно, поскольку не предусмотрено, что тип возвращаемого значения действительно соответствует возвращаемому значению.
Единственным преимуществом этого является то, что вам не нужно приводить возвращаемое значение таких общих методов поиска, которые могут возвращать любой тип.
Я бы сказал: используйте такие конструкции с осторожностью, потому что вы теряете почти всю безопасность типов и получаете только то, что вам не нужно писать явное приведение при каждом вызове get()
,
И да: это в значительной степени черная магия, которая взрывается во время выполнения и разрушает всю идею о том, чего должны достичь генерики.
Тип объявлен в методе. Ничего не поделаешь " <X>
"означает. Тип ограничен областью действия только для метода и имеет отношение к конкретному вызову. Причина, по которой ваш тестовый код компилируется, заключается в том, что компилятор пытается определить тип и будет выдавать жалобы только в том случае, если он не может. должны быть явными.
Например, объявление для Collections.emptySet()
является
public static final <T> Set<T> emptySet()
В этом случае компилятор может угадать:
Set<String> s = Collections.emptySet();
Но если это невозможно, вы должны набрать:
Collections.<String>emptySet();
Я просто пытался понять то же самое с классом 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, должен быть возвращен как String.
В случае, когда метод находится в том же классе, что и вы, я обнаружил, что должен вызывать его с помощью "this". Например:
this.<String>get("status");
Как уже говорили, это довольно небрежно и опасно для команды GXT.
BaseModelData вызывает непроверенные предупреждения при компиляции, потому что это небезопасно. При таком использовании ваш код будет генерировать ClassCastException во время выполнения, даже если он сам не имеет никаких предупреждений.
public String getExpireDate() {
return get("expiredate");
}
Интересная заметка из RpcMap (GXT API 1.2)
заголовок get:
public java.lang.Object get(java.lang.Object key)
Имея общий параметр <X>
там, что не подтверждено, имеет тот же эффект, за исключением того, что вам не нужно говорить "объект" повсюду. Я согласен с другим постером, это небрежно и немного опасно.
Да, это опасно. Обычно вы защищаете этот код следующим образом:
<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);
}