如何将Java泛型通配符与使用多个泛型参数的方法一起使用? [英] How do I use Java generic wildcards with methods taking more than one generic parameter?

查看:2417
本文介绍了如何将Java泛型通配符与使用多个泛型参数的方法一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  public static ,所以我们有一个像这样的泛型方法,它是依赖注入初始化的一部分: TI扩展了TS> void registerTransient(
Class TS< TS> serviceClass,Class< TI> implementationClass)
{
//
}

在某些时候,我们发现一个类可能不一定存在的情况。这是一个实现类,我们将注入多个关闭(所以服务类与实现类相同)。当然,您可以这样写:

 类<?> clazz = Class.forName(com.acme.components.MyPersonalImplementation); 
registerTransient(clazz,clazz);

IDEA没有问题,但是javac抱怨:

 错误:类TestTrash中的registerTransient方法不能应用于给定的类型; 
要求:Class< TS>,Class< TI>
找到:Class< CAP#1>,Class< CAP#2>
原因:推断的类型不符合声明的边界
推断:CAP#2
bound(s):CAP#1
其中TS,TI是类型变量:
TS扩展在方法< TS,TI> registerTransient(Class< TS>,< TI>)< TI>中给出的对象
TI扩展在方法< TS,TI> registerTransient(Class< TS> ; Class LT< TI>)
其中,CAP#1,CAP#2是新鲜的类型变量:
CAP#1从捕获?
CAP#2从捕获的对象中扩展对象?

什么给了?该方法要求第二个参数是第一个的子类。不管什么类碰巧是,它是两个参数的同一个类对象,而且我认为一个类总是可以从它自己分配的。这几乎就好像javac不必要地发明第二个通配符类型来用于第二个参数,然后哦,亲爱的,你在这里有两个通配符,所以我不能确定一个是否可以从另一个中分配。

解决方案

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



尝试将它明确地强制转换为Class,如:

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

这样编译器可以将 TS 绑定到 Object TI 扩展为Object的东西(尽管如此,它仍然会发出警告)。



IntelliJ的编译器不会抱怨这一点,这可能是由于某种优化,甚至可能是编译器错误。如果你想用稍微不同的方法来检查它,下面的代码仍然不能编译,即使它看起来很好:

  public class A {
static class B {}

static类C扩展B {}

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

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


查看这里。它呈现了Java类型系统的非凡视图(尽管非常密集)。

So we have a generic method like this, which is part of dependency injection initialisation:

public static <TS, TI extends TS> void registerTransient(
    Class<TS> serviceClass, Class<TI> implementationClass)
{
    //
}

At some point we found a case where a class might not necessarily be present. And it's an implementation class which we would be injecting multiple off (so the service class is the same as the implementation class.) Naturally you would write this like this:

Class<?> clazz = Class.forName("com.acme.components.MyPersonalImplementation");
registerTransient(clazz, clazz);

IDEA has no problems with this, but javac complains:

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 ?

What gives? The method requires the second parameter to be a subclass of the first. Irrespective of what class ? happens to be, it's the same class object for both parameters and a class is, I thought, always assignable from itself. It's almost as if javac is unnecessarily inventing a second wildcard type to use for the second parameter and then going "oh dear, you have two wildcards here, so I can't tell if one is assignable from the other."

解决方案

The problem is that Class<?> cannot be cast to any other type unless via an explicit cast (it actually becomes Class<#capture-... of ?> during capture conversion). As such, the compiler cannot (statically) match the type bounds of those captures with the parameterized types of the method definition.

Try casting it explicitly to Class first, like in:

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

This way the compiler can bind TS to Object and TI to something that extends Object (it will still emit a warning, though).

The fact that IntelliJ's compiler does not complain about this, might be due to some optimization or might even be a compiler bug. You should post it as such and wait for a reply.

If you wish to check it with a slightly different approach, the following will still not compile, even though it "looks" ok:

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);
    }
}

Have a look in here. It presents an extraordinary view of Java's type system (although quite dense).

这篇关于如何将Java泛型通配符与使用多个泛型参数的方法一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆