We have a cache framework which we use to wire application-specific cache types (such as this authentication cache shown below) to various implementations (e.g. ehcache, redis, memcached etc). The framework is just an abstraction layer to allow the application to define and manipulate its cache similar to a map of key-value pairs, while specifying its app-specific key class and value class.
So for example we have:
public class AuthenticationCache extends BaseAuthenticationCacheImpl<AuthenticationCacheKey, AuthenticationCacheEntry> {...}
public class AuthenticationCacheKey implements IAuthenticationCacheKey {...}
public class AuthenticationCacheEntry implements IAuthenticationCacheEntry {...}
and elsewhere in the application, the app overrides an abstract method which provides a Supplier for its cache:
@Override
protected <K extends IAuthenticationCacheKey, E extends IAuthenticationCacheEntry> Supplier<BaseAuthenticationCacheImpl<K, E>> getAuthCacheSupplier() {
Supplier<BaseAuthenticationCacheImpl<K, E>> supplier = () -> {
return new AuthenticationCache();
};
}
But this creates a compiler error:
Type mismatch: cannot convert from AuthenticationCache to
BaseAuthenticationCacheImpl
Generics are kicking my backside these days. Am I doing this completely wrong? Can I safely cast the supplier to (BaseAuthenticationCacheImpl<K,E>)
since I know after type erasure it’ll be the same runtime and I know that the concrete key/value classes of AuthenticationCache
satisfy K,E (e.g. extends IAuthenticationCacheKey/IAuthenticationCacheEntry) ?
2
Answers
You could cheat the compiler by using something like this:
The cast is technically safe, as long as you can guarantee that
K
andE
are alwaysAuthenticationCacheKey
andAuthenticationCacheEntry
, but the compiler can’t give you that guarantee.Assuming these classes:
A safe solution is to change the return type to:
As long as the
BaseAuthenticationCacheImpl
is just used to produce something that implementsIAuthenticationCacheKey
and something that implementsIAuthenticationCacheEntry
, but is not a consumer.Depending one how you actually use the type parameters of
BaseAuthenticationCacheImpl
you might even be able to just drop them completely and exchange them forIAuthenticationCacheKey
andIAuthenticationCacheEntry
directly. (Sometimes the best solution to a generics problem is to not use generics)