我正在尝试做某事,但这可能不是解决问题的最佳方法,或者也许我只是在语言设计中缺少了一些东西。基本上,我有一些类似的服务:
public class MyService<T extends MyAbstractClass>{}
作为服务器的配置,我想决定将哪个
MyAbstractClass
扩展名用作环境变量。在我的特定用例中,基本上是为我正在做的事情建立一个特定的数据源,但这在这里并不是太重要。我以为我可以做这样的事情,但是它给我语法错误,因此显然是不允许的。我不确定我是否能做我想完成的事情。
@Bean(name="myService")
public MyService<? extends MyAbstractClass> getMyService(){
String packageName = "com.my.project." + ConfigUtils.get(env, "mypackage") + "";
Class<? extends MyAbstractClass> clazz = (Class<? extends MyAbstractClass>) Class.forName(packageName);
return new MyService<clazz>();
}
我一直在寻找答案,但是我什至不知道如何在搜索引擎中正确地表达它才能找到答案。我应该如何尝试在Java中完成此操作?
最佳答案
这将永远无法工作:
new MyService<clazz>()
其中
clazz
是变量。new
调用中的类型参数只能是封闭方法或类的另一个类型参数(如果封闭方法不是静态的)或具体的类名。类型参数是只对编译时间有影响的实体,其引入是为了限制参数的类型并在通用类和方法中返回值,如果代码可能在运行时传递错误类型的值,则它们将在编译时失败,时间。
clazz
的值只能在运行时确定,因此编译器无法确定编译时的实际类。所以这就是为什么将包含变量的Class引用用作类型参数参数是不合法的,也没有任何意义的原因。事实是,限制返回的MyService实例的基础类型参数是什么并返回
MyService<? extends MyAbstractClass>
一样好。至少您可以仔细检查
clazz
实际上是否扩展了MyAbstractService,而不是简单地盲目相信:class ServiceClass {}
class MyService<T extends ServiceClass> {}
class Main {
public MyService<? extends ServiceClass> getMyService() throws Exception {
final String className = "com.my.project." + ConfigUtils.get(env, "mypackage");
@SuppressWarnings("unchecked")
final Class<? extends ServiceClass> clazz = (Class<? extends ServiceClass>) Class.forName(className);
if (!ServiceClass.class.isAssignableFrom(clazz))
throw new RuntimeException("bad configuration blah blah");
return new MyService<ServiceClass>();
}
}
请注意,
new MyService<ServiceClass>()
看起来不是很整洁,因为实际上clazz
通常是其他东西。 ServiceClass的后代,而不是ServiceClass本身的后代...之所以编译没有错误,是因为到目前为止,对于给定的代码,与在运行时返回的类型参数都转换为相同的代码无关紧要。但是,在更复杂的上下文中,当您要对某些对象执行某些操作以引用同一类型参数时,就会开始遇到编译时问题。因此从一开始就建议使用更整洁的基于类型参数的解决方案:
class ServiceClass {}
class MyService<T extends ServiceClass> {}
class Main {
public <S extends ServiceClass> MyService<S> getMyService() throws Exception {
final String className = "com.my.project." + ConfigUtils.get(env, "mypackage");
@SuppressWarnings("unchecked")
final Class<S> clazz = (Class<S>) Class.forName(className);
if (!ServiceClass.class.isAssignableFrom(clazz))
throw new RuntimeException("bad configuration blah blah");
return new MyService<S>();
}
}
现在,我认为您不需要在MyService中的某个位置保留对服务类的引用是很奇怪的……也许您最终将需要编写如下代码:
class ServiceClass {}
class MyService<S extends ServiceClass> {
private final Class<S> clazz;
MyService(Class<S> clazz) {
this.clazz = clazz;
}
Class<S> serviceClass() {
return clazz;
}
}
class Main {
public <S extends ServiceClass> MyService<S> getMyService() throws Exception {
final String className = "com.my.project." + ConfigUtils.get(env, "mypackage");
@SuppressWarnings("unchecked")
final Class<S> clazz = (Class<S>) Class.forName(className);
if (!ServiceClass.class.isAssignableFrom(clazz))
throw new RuntimeException("bad configuration blah blah");
return new MyService<S>(clazz);
}
}
此时,
S
中的类型参数getMyService
开始是必需的。顺便说一句,自Java 7(我认为)以来,您不需要在新语句中指定
S
,这将由编译器推导:return new MyService<>(clazz);
关于java - 创建新实例时,将变量用于通用类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42772701/