gpt4 book ai didi

java - 创建新实例时使用泛型类型的变量

转载 作者:太空宇宙 更新时间:2023-11-04 11:41:05 26 4
gpt4 key购买 nike

我正在尝试做某事,但这可能不是最好的方法,或者也许我只是在语言设计中遗漏了一些东西。基本上,我有一些类似这样的服务:

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的值只能在运行时确定,因此编译器无法在编译时确定实际的类是什么。因此,这就是为什么使用包含变量作为类型参数参数的类引用是不合法的,也没有任何意义。

事实是,限制返回的 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);
}
}

此时类型参数 SgetMyService开始变得有必要了。

顺便说一下,从 Java 7 开始(我认为)你不需要指定 S在新的语句中,编译器将推断出:

return new MyService<>(clazz); 

关于java - 创建新实例时使用泛型类型的变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42772701/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com