如何将Java通用通配符与接受多个通用参数的方法一起使用?

如何将Java通用通配符与接受多个通用参数的方法一起使用?,java,generics,subclass,bounded-wildcard,Java,Generics,Subclass,Bounded Wildcard,因此,我们有这样一个通用方法,它是依赖项注入初始化的一部分: public static <TS, TI extends TS> void registerTransient( Class<TS> serviceClass, Class<TI> implementationClass) { // } 公共静态无效寄存器transient( 类服务类、类实现类) { // } 在某个时候,我们发现了一个类不一定存在的情况。这是一个实现类,我们

因此,我们有这样一个通用方法,它是依赖项注入初始化的一部分:

public static <TS, TI extends TS> void registerTransient(
    Class<TS> serviceClass, Class<TI> implementationClass)
{
    //
}
公共静态无效寄存器transient(
类服务类、类实现类)
{
//
}
在某个时候,我们发现了一个类不一定存在的情况。这是一个实现类,我们将注入多个off(因此服务类与实现类相同。)您自然会这样写:

Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz, clazz);
Class clazz=Class.forName(“com.acme.components.MyPersonalImplementation”);
寄存器瞬态(clazz,clazz);
IDEA对此没有问题,但javac抱怨:

error: method registerTransient in class TestTrash cannot be applied to given types;
required: Class<TS>,Class<TI>
found: Class<CAP#1>,Class<CAP#2>
reason: inferred type does not conform to declared bound(s)
inferred: CAP#2
bound(s): CAP#1
where TS,TI are type-variables:
TS extends Object declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
TI extends TS declared in method <TS,TI>registerTransient(Class<TS>,Class<TI>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
错误:TestTrash类中的方法registerTransient无法应用于给定类型;
必修课:课堂,课堂
发现:类,类
原因:推断的类型不符合声明的绑定
推断:第2章
绑定:第1章
其中TS、TI是类型变量:
TS扩展方法registerTransient(类,类)中声明的对象
TI扩展方法registerTransient(类,类)中声明的TS
其中,CAP#1、CAP#2是新类型变量:
CAP#1将对象从捕获扩展到?
CAP#2将对象从捕获扩展到?

有什么好处?该方法要求第二个参数是第一个参数的子类。不管什么类
恰好是,对于这两个参数,它都是同一个类对象,而且我认为,一个类总是可以从自身赋值的。这几乎就像javac不必要地发明了第二个通配符类型来用于第二个参数,然后说“哦,天哪,这里有两个通配符,所以我无法区分一个通配符和另一个通配符是否可赋值。”

问题是,除非通过显式转换,否则不能将
类转换为任何其他类型(在捕获转换期间,它实际上变成了
Class
)。因此,编译器不能(静态地)将这些捕获的类型边界与方法定义的参数化类型匹配

尝试先将其显式转换为类,如:

registerTransient((Class<Object>)clazz, clazz); 

查看一下。它展示了Java类型系统的非凡视图(尽管非常密集)。

您遇到的问题是,根据JLS 7,每个方法参数的捕获转换都是单独进行的:

如果表达式名称出现在要进行赋值转换、方法调用转换或强制转换的上下文中,则表达式名称的类型是捕获转换()后声明的字段、局部变量或参数的类型

在您的例子中,“表达式名称”是标识符
clazz
。正如编译器输出所示,它将按照JLS的要求被捕获两次

处理此问题的方法是引入一个助手方法,该方法将通配符绑定到类型变量:

private static <T> void registerTransient(Class<T> serviceAndImplClass)
{
    registerTransient(serviceAndImplClass, serviceAndImplClass);
}
private static void register transient(类serviceAndImplClass)
{
registerTransient(serviceAndImplClass、serviceAndImplClass);
}
使用通配符调用此新方法将有效:

Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz);
Class clazz=Class.forName(“com.acme.components.MyPersonalImplementation”);
注册瞬态(clazz);

我看到你在一篇评论中提到了这个解决方法。这可能看起来很奇怪,但实际上这是语言设计者想要的方法。

我不知道为什么会这样做,但是你能重载并创建另一个签名来接受两个相同类型的签名吗?你能删除类型而不是使用通配符吗?比如
Class
我想,我知道它会变成一个捕获,但我害怕的是,它变成了两个捕获,尽管我在代码中只使用了一个通配符。在我看来,IntelliJ可能已经变成了一个只需以更合理的方式实现检查器,即可“正确完成”。在您的上下文中,
Class
表示
Class
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz);