当bean类型参数是类型变量

时间:2015-04-23 01:32:22

标签: cdi wildfly type-variables

以下代码片段与“weld-core-1.1.5.AS71.Final.jar”(JBoss 7.1.1使用的那个)一起使用,但不适用于“weld-core-impl-2.2”。 6.Final.jar“(wildfly 8.2使用的那个)。

public class Client<T> {
    public interface Spi<T> {
        T getSomething();
    }

    @Inject
    private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object>

}

public class SpiImpl implements Client.Spi<Integer> {
    @Override
    public Integer getSomething() {
        return 5;
    }
}

为什么呢? CDI 1.2规范:

  

参数化bean类型被认为可分配给参数化   如果它们具有相同的原始类型和每个参数,则为必需类型:

     
      
  1. 所需的类型参数和bean类型参数是实际的   具有相同原始类型的类型,如果类型是参数化的,   bean类型参数可分配给所需的类型参数   根据这些规则,或
  2.   
  3. 必需的类型参数是通配符,bean类型参数是实际类型,实际类型可分配给上层   如果有的话,绑定(如果有的话)通配符并且可以从下限赋值   任何,通配符,或
  4.   
  5. 所需的类型参数是通配符,bean类型参数是类型变量,类型变量的上限是   可转让或转让自上限(如果有的话)   通配符并可从通配符的下限(如果有)分配,   或
  6.   
  7. 所需的类型参数是实际类型,bean类型参数是类型变量,实际类型可分配给   类型变量的上限(如果有),或
  8.   
  9. 必需的类型参数和bean类型参数都是类型变量,并且所需类型参数的上限是   可分配给bean类型参数的上限(如果有)。
  10.   

所以,根据第4项,上面的代码应该有效,不应该吗?

编辑1:&lt; - 请先查看编辑2 。在那里,我注意到CDI实现正在按照我的预期应该做,但我错误地认为它没有。

Antonin Stefanutti回答是对的:上述规范的第4项不适用。但是,如果在解决了inyectión点时已知类型变量T的实际类型,则可以获得Spi的正确实例。

Supouse Client类是抽象的,实现指定类定义中类型参数的类型。在这种情况下,可以发现实际类型,并且适用规范的第4项。

如何解决?通过对Class#getGenericInterfaces()实例的实际Class使用Client操作进行内省。像这样:

public class Test {

@Inject
private ClientImpl clientImpl;

public abstract static class Client<T> {
    public interface Spi<T> {
        T getSomething();
    }

    // @Inject inject don't work because CDI doesn't require it even when the actual type of T can be discovered. So, instead, I initialized it programaticaly in the postConstruct()
    private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object> // Not true, later, in "edit 2" I noteced it works fine. Forget all this. Sorry.

    @Inject
    private Instance<Spi<?>> spiFinder;

    /**Initializes the spi instance variable programaticaly */
    @PostConstruct
    private void postConstruct(){
        ParameterizedType clientType = (ParameterizedType)this.getClass().getGenericSuperclass();
        Type clientTypeParamter = clientType.getActualTypeArguments()[0];
        Class<T> clientParameterClass = (Class<T>)clientTypeParamter;

        final Iterator<Spi<?>> iterator = spiFinder.iterator();
        while (iterator.hasNext()) {
            Spi<?> spiCandidate = iterator.next();
            ParameterizedType spiCandidateType = (ParameterizedType)spiCandidate.getClass().getGenericInterfaces()[0];
            Type spiCandidateTypeParameter = spiCandidateType.getActualTypeArguments()[0];
            Class<?> spiCandidateParameterClass = (Class<?>)spiCandidateTypeParameter;
            if( clientParameterClass.isAssignableFrom(spiCandidateParameterClass)) {
                if( spi != null)
                    throw new AmbiguousResolutionException();
                spi = (Spi<T>)spiCandidate;
            }
        }
        if( spi == null)
            throw new UnsatisfiedResolutionException();
    }
}

@Dependent
public static class SpiImpl_1 implements Client.Spi<Integer> {
    @Override
    public Integer getSomething() {
        return 5;
    }
}

@Dependent
public static class SpiImpl_2 implements Client.Spi<Double> {
    @Override
    public Double getSomething() {
        return 5.0;
    }
}

@Dependent
public static class ClientImpl extends Client<Integer> {}

}

以上代码有效(SpiImpl_1的实例被注入spi实例变量),那么,为什么CDI不能为我们做这个工作呢?通过instrospection,当具体类指定扩展超类的类型参数的实际类型时,可以发现在封闭类/接口定义中声明的每个类型参数的实际类型。

编辑2 对不起,忘了我说过的一切。 CDI确实在第二个示例中注入了Client的正确实例,而不需要我添加的编程初始化。它不会抛出“WELD-001408:类型Spi不满意的依赖关系”,我说它确实如此。我错了。再次抱歉。 我应该删除这个问题吗?

1 个答案:

答案 0 :(得分:1)

在您的示例中,必需类型参数是一个类型变量T,而不是实际类型,因此项目#4不适用。实际上,在您的示例中,满足了规范中提到的参数化bean类型可赋值为参数化所需类型的任何条件,这最终意味着不满足依赖性。

这与CDI-517有某种关系,虽然不完全相同,但澄清可以解释Weld 1.x和Weld 2.x之间的行为变化,因为规范已经澄清。

正如在该讨论中所提到的,从Java语言的角度来看,Spi<Integer> 可分配Spi<T>是不正确的。