我正在开发一个基于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 非常有点过时了。
答案 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();
}