Java泛型:泛型定义为仅返回类型


68

我正在查看GWT的一些GXT代码,并且遇到了泛型的这种用法,我在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");
}

它们编译并且不显示任何错误或警告。我认为至少我必须进行转换才能使其正常工作。

这是否意味着泛型允许一个魔术返回值,该返回值可以是任何值,并且会在运行时爆炸?这似乎与仿制药应该做的相反。谁能给我解释一下,并且可能给我链接一些说明得更好的文档?我已经阅读了Sun的23页关于泛型的pdf文件,每个返回值的示例都在类级别定义或在传入的参数之一中定义。


感谢您到目前为止的回答!如果有代表,我会全力以赴(尤其是saua,非常有帮助)。周转时间给人留下了深刻的印象。我仍然很乐意看到一些参考资料,可能会更详细地解释这一点,但我想我比以前更了解这一点
Jason Tholstrup


随时更新。我已经好几年没有使用gxt了。
杰森·托尔斯楚普

FXMLLoader的某些方法也利用了这一点。这是特别有用的,因为类型转换总是由用户完成。如果也可以添加此行为,那就更好了ObjectInputStream::readObject
Mordechai

Answers:


55

该方法返回您期望的类型(<X>在方法中定义,并且是绝对不受限制的)。

这非常非常危险,因为没有规定返回类型实际上与返回值匹配。

这样做的唯一好处是,您不必强制转换可以返回任何类型的此类通用查找方法的返回值。

我会说:谨慎使用此类构造函数,因为您几乎失去了所有类型安全,而获得的好处是,不必在每次调用时都编写显式强制转换get()

是的:这几乎是一种黑魔法,在运行时会炸毁,打破了泛型应该实现的整个想法。


6
当类型还用于限定其中一种方法的泛型类型时,此构造要安全得多-这意味着您无需显式强制转换。这样,就没有机会在运行时崩溃。
比尔·米歇尔

3
就GWT而言,这可能是设计使然;GWT可以编译为JavaScript,众所周知,JavaScript是动态的,弱类型的。
妮可,2010年

39

在方法上声明类型。这就是“ <X>”的意思。然后将类型的范围限定为仅方法,并且与特定的调用相关。您的测试代码编译的原因是,编译器会尝试确定类型,并且只会在无法确定类型时进行投诉。在某些情况下,您必须明确。

例如,对于该声明Collections.emptySet()

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

在这种情况下,编译器可以猜测:

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

但是,如果不能,则必须键入:

Collections.<String>emptySet();

旁注:Java的最新版本(8+)通过类型推导变得更加智能,像上一个那样的示例不再需要显式类型(取决于它们的使用位置)。
Joachim Sauer

5

我只是想用GXT类弄清楚同一件事。具体来说,我试图用以下方法调用签名的方法:

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

要从您的代码中调用上述方法并将其强制转换为字符串,请执行以下操作:

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

上面的代码将调用get方法,并告诉它X返回的类型应作为String返回。

在该方法与您位于同一类的情况下,我发现必须用“ this”来调用它。例如:

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

正如其他人所说的那样,这对于GXT团队而言是相当草率和危险的。


2

BaseModelData在编译时会引发未经检查的警告,因为它是不安全的。这样使用,即使您自己没有任何警告,您的代码也将在运行时引发ClassCastException。

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

1
好的,我试了一下:仅当运行时类型不是字符串类型时,它才会引发异常。如果是字符串,则可以。您刚刚将演员表移至get方法。
Jason Tholstrup

1
究竟。如果您在没有警告的情况下编译代码,则泛型将保证您永远不会收到ClassCastException;通过忽略警告,BaseModelData放弃了这种保证。
erickson

2

有趣的注解,来自RpcMap(GXT API 1.2)

获取的标头:

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

<X>其中具有未实例化的泛型参数具有相同的效果,除了不必在所有位置都说“对象”。我同意其他海报,这很草率,有点危险。


0

是的,这很危险。通常,您可以像下面这样保护此代码:

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

2
为什么这样更好?它仍然可以以不同的方式在运行时崩溃。
David Roussel

3
通过插入两个连续的运行时炸弹来保护爆炸!首先,强制转换将作为异常而爆炸...,然后,如果这还不够,则断言(无论如何将无法运行)将使整个程序爆炸!,更加复杂...没有额外的好处...- 1
Newtopian 2010年

3
公共<T> T get(int i,Class <T> type){Object val = get(i); 如果(type.isInstance(val)){返回type.cast(val); 返回null;}
tar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.