Java泛型 - 获取类的实例

3

我有一个名为Data的抽象类,其中有一个getInstance()方法,它应该返回Data具体子类的实例。

我想将一个字符串传递给getInstance方法,这个字符串将定义返回哪个类。

目前为止我有:

public abstract class Data {

  private Map<String, Data> instances;

  public <T extends Data> T  getInstance(Class<T> type, String dataName) {
    return type.cast(instances.get(dataName)); 
  }

}

getInstance()方法将在instances映射中查找正确的实例。我认为只要该映射由Spring填充,这应该是可以的,但调用者必须将Class<T> type参数与String dataName参数匹配。

是否有任何方法可以删除Class<T> type参数并且不会出现泛型警告?


1
这看起来不对。考虑其他解决方案吧。 - Philipp Sander
我想要,如果我能想到一个。 - NickJ
你使用字符串来标识类型的理由是什么?如果可以避免,就不要这样做。依赖于字符串(无法在编译时检查)来标识具体类型会在运行时为系统带来巨大的风险。 - cfeduke
2个回答

2
不,如果您尝试将对象转换为泛型类型并不知道运行时类型,则始终会收到警告。并且,没有办法获得泛型类的实例而不提供它作为参数 - 泛型在运行时被擦除。
没有显式转换且不出现警告的唯一方法是返回Object或Data(取决于Map),并要求用户进行转换:
public Data getInstance(String dataName) {
    return instances.get(dataName); 
}
// OR
public Object getInstance(String dataName) {
    return instances.get(dataName); 
}

在我看来,为了方便起见,最好提供两种方法:Data getInstance(String)<T extends Data> T getInstance(Class<T>, String)。这基本上也是OSGI在BundleContext类中所做的,因此可以获取服务引用,但不同之处在于无法使用任意ID获取服务(ID始终为类名)。请注意保留HTML标签。
ServiceReference<?> BundleContext#getServiceReference(java.lang.String clazz) 
ServiceReference<S> BundleContext#getServiceReference(java.lang.Class<S> clazz) 

1
你可以完全跳过class参数。
public <T extends Data> T getInstance(String dataName) {
    return (T)instances.get(dataName);
}

这仍会生成警告(可以抑制)。两种方法都会在实际类与预期类型不同时引发运行时异常。在我的示例中,编译类型推断在某些情况下无法正常工作,您需要在调用时指定类型,如下所示:Data.<SubClass>getInstance("name"); 可以有一个解决方案,如果键的数据子类型不正确,则返回null。
public <T extends Data> T getInstance(Class<T> clazz, String dataName) {
    Data d = instances.get(dataName);
    if (d != null && clazz.isAssignableFrom(d.getClass())) {
        return (T)d;
    } else {
        return null;
    }
}

如果映射中的值不是正确的类T或其子类,则此代码将返回null。

该方法基本上是不安全的类型。调用代码可以任意选择返回类型。 - newacct
是的,但使用Class参数的解决方案也是如此,因为您可以这样调用DataSubclass1 s = Data.getInstance(DataSubclass1.class, "keyForDataSubclass2Instance"),这将抛出运行时异常。 - RokL
在这两种情况下,运行时转换都是在getInstance方法中的返回语句中进行的,因此这将是两种情况下异常的根源。 - RokL
return (T)instances.get(dataName); 是对 T 的运行时转换。 - RokL
不,它不是。类型 T 在运行时不存在,除非你有一个在运行时代表它的对象。它只会是对 Data 的运行时转换,即 T 的擦除。 - newacct
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接