首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在带有多个泛型参数的方法中使用Java泛型通配符?

如何在带有多个泛型参数的方法中使用Java泛型通配符?
EN

Stack Overflow用户
提问于 2012-01-20 08:07:52
回答 2查看 2.9K关注 0票数 2

所以我们有一个像这样的泛型方法,它是依赖注入初始化的一部分:

代码语言:javascript
复制
public static <TS, TI extends TS> void registerTransient(
    Class<TS> serviceClass, Class<TI> implementationClass)
{
    //
}

在某种程度上,我们发现了一个类可能不一定存在的情况。这是一个实现类,我们将注入多个off (因此服务类与实现类相同)。很自然,你会这样写:

代码语言:javascript
复制
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz, clazz);

IDEA对此没有问题,但javac抱怨道:

代码语言:javascript
复制
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 ?

怎么回事?该方法要求第二个参数是第一个参数的子类。不管?是什么类,它是两个参数的同一个类对象,而且我认为,类总是可以从自身赋值的。这几乎就像是javac不必要地发明了第二个通配符类型用于第二个参数,然后说“哦,天哪,这里有两个通配符,所以我不知道其中一个是不是可以赋值的。”

EN

回答 2

Stack Overflow用户

发布于 2012-01-20 10:15:51

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

尝试将其显式转换为Class first,如下所示:

代码语言:javascript
复制
registerTransient((Class<Object>)clazz, clazz); 

通过这种方式,编译器可以将TS绑定到Object,将TI绑定到扩展Object的对象(尽管它仍然会发出警告)。

事实上,IntelliJ的编译器没有对此进行抱怨,这可能是由于某些优化,甚至可能是编译器错误。你应该把它贴出来,然后等待回复。

如果你想用一种稍微不同的方法来检查它,下面的代码仍然不会被编译,即使它“看起来”没问题:

代码语言:javascript
复制
public class A {
    static class B {}

    static class C extends B {}

    static <T, R extends T> void method(final Class<T> t, final Class<R> r) {}

    public static final void main(String... args) {
        B b = new B();
        C c = new C();
        Class<?> cb = b.getClass();
        Class<?> cc = c.getClass();
        method(cb, cc);
    }
}

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

票数 2
EN

Stack Overflow用户

发布于 2013-07-03 11:55:55

您遇到的问题是,根据JLS7 §6.5.6.1 Simple Expression Names,捕获转换针对每个方法参数单独发生

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

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

处理这个问题的common technique是引入一个帮助器方法,它将通配符绑定到一个类型变量:

代码语言:javascript
复制
private static <T> void registerTransient(Class<T> serviceAndImplClass)
{
    registerTransient(serviceAndImplClass, serviceAndImplClass);
}

使用通配符调用这个新方法将会起作用:

代码语言:javascript
复制
Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz);

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8935185

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档