Java和泛型类型边界

时间:2016-02-12 00:30:32

标签: java generics lambda java-8

一些背景

我正在开发一个基于Lambda表达式的小型DI容器。 我有代表lambda的接口:

@FunctionalInterface
public interface LambdaOp<T> {
    T resolve();
}

这是(非常简化的)容器:

public class Container {
    private final Map<Class<?>, LambdaOp<?>> bindings;

    public <T> void bind(Class<T> clazz, LambdaOp<? extends T> lambda) {
        bindingd.put(clazz, lambda);
    }

    public <T> T resolve (Class<T> clazz) {
        LambdaOp<?> lambda = bindings.get(clazz);
        return (T) lambda.resolve(); 
    } 

}

通过这种实现,我可以做类似的事情:

container.bind(
    FooInterface.class,
    () -> {
        FooImplementation foo = new FooImplementation();
        foo.setParameterA(paramA);
        //do whatever needed to configure it
        return foo;  
    }
);

FooInterface foo2 = container.resolve(FooInterface.class);

它工作正常并且很好,因为编译器不允许我这样做:

container.bind(
    FooInterface.class,
    () -> new BarImplementation() //compiler error; Bar does not implement Foo
);

问题

绑定Map本身并不保证LambdaOp将具有扩展用作键的类的泛型类型。

private final Map<Class<?>, LambdaOp<?>> bindings;

因此,我在以下行收到未经检查的警告

return (T) lambda.resolve();

我认为绑定方法签名足以保证凝聚力(是吗?)但仍然感觉到一些难闻的气味。

有没有办法以更有凝聚力的方式实现它?

修改

完整代码位于github。我最近做了很多改动,README.md 非常有点过时了。

2 个答案:

答案 0 :(得分:5)

为什么不使用Class clazz键进行投射?你的逻辑已经保证演员阵容总能正常工作

return clazz.cast( lambda.resolve() );

编辑以澄清: 使用Class对象的原因是没有警告,但简单的通用强制转换不是因为在运行时使用泛型强制转换,泛型类型也可以被删除到Object,因此编译器可以& #39; t保证演员表适用于预期的类型。另一方面,Class对象将具有进行强制转换所需的所有信息,因此编译器可以安全地允许它用于泛型类型。

假设你当然不会吝啬杂乱无章。

答案 1 :(得分:3)

没有办法做到这一点,编译器会自然地找出并保证泛型类型是正确的。但是,您可以告诉编译器关闭它,在这种情况下,这样做是合适的,因为您的实现确实提供了相关的保证。

public <T> T resolve (Class<T> clazz) {
    @SuppressWarnings("unchecked")
    LambdaOp<? extends T> lambda = (LambdaOp<? extends T>) bindings.get(clazz);
    return lambda.resolve(); 
}