I am developing a small DI Container based on Lambda expressions. I have the interface representing the lambda:
@FunctionalInterface
public interface LambdaOp<T> {
T resolve();
}
And this is the (very simplified) Container:
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();
}
}
With this implementation I can do something like that:
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);
It is working ok and it is nice because the compiler won't let me do something like this:
container.bind(
FooInterface.class,
() -> new BarImplementation() //compiler error; Bar does not implement Foo
);
The bindings Map itself does not guarantee that LambdaOp will have a generic type which extends the Class used as key.
private final Map<Class<?>, LambdaOp<?>> bindings;
As a result I receive an unchecked warning at the line:
return (T) lambda.resolve();
I suppose that the bind method signature is enough to guarantee the cohesion (is it?) but still feel some bad smell.
Is there any way to implement it on a more cohesive way?
EDIT:
The complete code is at github. I have made a lot of changes recently and README.md is very a little bit outdated.
why not just cast using the Class clazz
key? Your logic already guarantees that the cast will always work anyway
return clazz.cast( lambda.resolve() );
edit for clarification:
the reason that using the Class
object works without a warning but the simple generic cast does not is because at runtime with the generic cast, the generic type may as well be erasured to Object
and therefore the compiler can't make guarantees that the cast will work for the intended type. The Class
object on the other hand will have all the information needed to do a cast and so the compiler can safely allow it for generic types.
Assuming you don't go out of your way to mess with the generics of course.
There is no way to do this such that the compiler will naturally figure out and guarantee on its own that the generic type is correct. You can, however, tell the compiler to shut up about it, and in this case it is appropriate to do so because your implementation does provide the relevant guarantee.
public <T> T resolve (Class<T> clazz) {
@SuppressWarnings("unchecked")
LambdaOp<? extends T> lambda = (LambdaOp<? extends T>) bindings.get(clazz);
return lambda.resolve();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With