So the short, simple answer is that a constructor can only return its own type. No subtypes, no other classes, no reused instances, no null
—only a new instance of that type. So you're looking for a solution that operates outside the confines of a constructor here. The simplest and most common workaround is to write a static factory method (usually named newInstance
or getInstance
) which returns any new or existing instance of the enclosing class and can return a subclass or null
without trouble.
Your points about your solution 1 and 2 are valid. It'd be great to avoid making the data types aware of the UI, and in your situation (with only four types) I'd probably opt for your solution 2. If you have operations that will vary among those types—which is a pretty common requirement in a GUI that puts a mixture of types into a tree—Bittenus's solution is probably worth it. (It's a lot of code to handle if you only need to do this sort of thing once.)
If you somehow expect your type count to grow but your operations to never grow, one alternative is to extract the polymorphic creation into a separate Factory, which might look like this:
class MyAppTreeNode {
interface Factory {
MyAppTreeNode create(DataType type);
}
}
class AlphaTreeNode extends MyAppTreeNode {
static class Factory implements MyAppTreeNode.Factory {
@Override public AlphaTreeNode create(DataType type) {
// Remember, in an override your return types can be more-specific
// but your parameter types can only be less-specific
return new AlphaTreeNode((DataTypeAlpha) type);
}
}
}
Then you can just make a map (though consider Guava's ImmutableMap for better semantics):
private static Map<Class<?>, MyAppTreeNode.Factory> factoryMap = new HashMap<>();
static {
factoryMap.put(DataTypeAlpha.class, new AlphaTreeNode.Factory());
// ...
}
public static createTreeNode(DataType type) {
return factoryMap.get(type.getClass()).create(type);
}
More trouble than it's worth? Probably, in most cases. But bear in mind that it's probably the best that Guice can get you, as well. Guice has some ability to auto-generate the Factory implementation for you, but you'll still need to map DataType
to MyAppTreeNode.Factory
one way or another, and it's going to have to live in a Map, a conditional, or the double indirection that powers the Visitor pattern.
Hope this helps, if only to endorse the answers you already have!