I've a structure like this:
abstract class MyDomain{...}
abstract class FooDomain extends MyDomain{...}
abstract class BarDomain extends MyDomain{...}
class FirstConcreteBarDomain extends BarDomain{...}
class SecondConcreteBarDomain extends BarDomain{...}
I need a factory that creates MyDomain
objects. My first attempt was this:
public interface ISpecializedObjectsFactory {
public <T extends MyDomain> T create(Class<?> clazz);
}
Implementend as:
public class FirstSpecializedObjectsFactory implements ISpecializedObjectsFactory {
@Override
public <T extends MyDomain> T create(Class<?> clazz) {
if(clazz.equals(BarDomain.class))
return new FirstBarDomain();
throw new InvalidParameterException();
}
Same for the SecondBarDomain
.
FIRST QUESTION: Why this is generating an error that says that it cannot cast FirstBarDomain
to T
?
After this error I've introduced a cast: return (T) new FirstBarDomain();
.
The problem is that the cast is unsafe and I want to be confident for the result, so I've introduced another constraint (assuming that each MyDomain
object have always 2 levels of derivation):
public <T extends AnagrafeDomain, S extends T> S create(Class<T> clazz)
SECOND QUESTION: Assuming that this factory is the only entry point where MyDomain
objects are created, and that the calls to the factory never use the concrete classes (but are always like: BarDomain subj = SpecializedObjectsFactory.getFactory().create(BarDomain.class);
), the question is: is this new version safe?
The reason the cast is unsafe is because of this particular line:
public <T extends MyDomain> T create(Class<?> clazz) {
This infers the return type from the call site; in other words, consider the following class:
public abstract class MyFakeDomain extends MyDomain { }
The following code will then compile, but fail at runtime:
ISpecializedObjectsFactory factory = new FirstSpecializedObjectsFactory();
MyFakeDomain broken = factory.create(BarDomain.class);
This will throw a ClassCastException because of the type inference; the inferred type will be MyFakeDomain
, resulting in an attempt to cast FirstBarDomain
to MyFakeDomain
, which is an illegal cast - hence the unsafe warning.
The type inference is also the reason why the cast must be present; whilst FirstBarDomain
is definitely a subclass of MyDomain
, we do not know if it is of type T
, as T
could be any MyDomain
subclass, not necessarily FirstBarDomain
.
However, if the caller is careful, your code will work fine - whether you consider this acceptable or not is up to you.
This gives us the answer to your second question: using BarDomain
as the type to be inferred will not always be safe, as it could be another subclass of MyDomain
. The only type that would be always safe here is MyDomain
- however, if you are planning on only using MyDomain
as the type, you might as well remove the generic type bound and just make the return type MyDomain
.