Fluent API with inheritance and generics - java

I'm writing a fluent API to configure and instantiate a series of "message" objects. I have a hierarchy of message types.
To be able to access method of subclasses when using the fluent API, I used generics to parametrize the subclasses and make all fluent methods (that start with "with") return the generic type. Note that I omitted most of the body of the fluent method; a lot of configuration goes on in them.
public abstract class Message<T extends Message<T>> {
protected Message() {
}
public T withID(String id) {
return (T) this;
}
}
The concrete subclasses redefine the generic type similarly.
public class CommandMessage<T extends CommandMessage<T>> extends Message<CommandMessage<T>> {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public T withCommand(String command) {
return (T) this;
}
}
public class CommandWithParamsMessage extends
CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, paramValue);
return this;
}
}
This code works, i.e. I can instantiate any of the classes and use all fluent methods:
CommandWithParamsMessage msg = CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
Calling the fluent methods in any order is a major goal here.
However, the compiler warns that all return (T) this are unsafe.
Type safety: Unchecked cast from Message to T
I'm unsure how I could reorganize the hierarchy to make this code truly safe. Even though it works, the use of generics in this fashion feels really convoluted.
Especially, I'm not able to foresee situations where runtime exceptions will happen if I just ignore the warnings.
There will be new message types, so I need to keep the code extensible.
If the solution is to avoid inheritance altogether I would also like to obtain suggestion of alternatives.
There are other questions here on SO that address a similar issue. They point to a solution where all intermediate classes are abstract and declare a method like protected abstract self(). Still, in the end it's not safe.

Your code is fundamentally an unsafe use of Generics. For example, if I write a new class which extends message, say Threat, and has a new method doSomething(), and then I create a message parameterised by this new class and it creates an instance of Message, and then attempts to Cast it to its subclass. However, since it is an instance of Message, and not of Threat, an attempt to call this message will cause an Exception. Since it is not possible for Message to doSOmething().
Further, its also unnecessary to use Generics here. Plain old inheritance will work fine. Since sub types can override methods by making their return types more specific, you can have:
public abstract class Message {
protected Message() {
}
public Message withID(String id) {
return this;
}
}
And then
public class CommandMessage extends Message {
protected CommandMessage() {
super();
}
public static CommandMessage newMessage() {
return new CommandMessage();
}
public CommandMessage withCommand(String command) {
return this;
}
}
This will work fine, on the understanding that you call your arguments in the right order:
CommandWithParamsMessage.newMessage()
.withID("do")
.withCommand("doAction")
.withParameter("arg", "value");
will fail, but
CommandWithParamsMessage.newMessage().withParameter("arg", "value")
.withCommand("doAction").withID("do")
Will succeed, since it only "up types", finally returning a "message" class. If you want it not to "uptype", then simply overwrite the inherited commands, and now you can call the methods in any order, since they are all return the original type.
E.g.
public class CommandWithParamsMessage extends
CommandMessage {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName,
String paramValue) {
contents.put(paramName, paramValue);
return this;
}
#Override
public CommandWithParamsMessage withCommand(String command){
super.withCommand(command);
return this;
}
#Override
public CommandWithParamsMessage withID(String s){
super.withID(s);
return this;
}
}
Now you will fluently return a CommandWithParamsMessage with either of the two fluent calls above.
Does this solve your problem, or have I misunderstood your intent?

I've done something like this before. It can get ugly. In fact, I've tried it more times than I've used it; usually it gets erased and I try to find a better design. That said, to help you move a little further down the road try this:
Have your abstract classes declare a method like:
protected abstract T self();
This can replace this in your return statements. The subclasses will be required to return something that matches the bound for T -- but it doesn't guarantee that they return the same object.

If you change the signatures like this you should neither get any warnings nor do you need any casts:
abstract class Message<T extends Message<T>> {
public T withID(String id) {
return self();
}
protected abstract T self();
}
abstract class CommandMessage<T extends CommandMessage<T>> extends Message<T> {
public T withCommand(String command) {
// do some work ...
return self();
}
}
class CommandWithParamsMessage extends CommandMessage<CommandWithParamsMessage> {
public static CommandWithParamsMessage newMessage() {
return new CommandWithParamsMessage();
}
public CommandWithParamsMessage withParameter(String paramName, String paramValue) {
// do some work ...
return this;
}
#Override protected CommandWithParamsMessage self() {
return this;
}
}

The compiler warns you of this unsafe operation, because it cannot factually check the correctness of your code. This makes it, as a matter of fact, unsafe and there is nothing you can do to prevent this warning. Even though an unsafe operation is not compile-checked, it can still be legitimate at run time. If you circumvent the compiler check, it is however your job to validate your own code for its use of correct types which is what the #SupressWarning("unchecked") annotation is for.
To apply this to your example:
public abstract class Message<T extends Message<T>> {
// ...
#SupressWarning("unchecked")
public T withID(String id) {
return (T) this;
}
}
is fine, because you can as a matter of fact tell with certainty that this Message instance is always of the type that is represented by T. But the Java compiler cannot (yet). As with other suppression warnings, the key to using the annotation is to minimize its scope! Otherwise, you can easily retain the annotation suppression by accident after you made code changes that render your former manual check for type safety as invalid.
As you only return a this instance, you can however easily outsource the task to a specific methods as recommended in another answer. Define a protected method like
#SupressWarning("unchecked")
public T self() {
(T) this;
}
and you can always call the mutator like here:
public T withID(String id) {
return self();
}
As another option, and if it is possible for you to implement, consider an immutable builder which only exposes its API by interfaces but implements a full builder. This is how I normally build fluent interfaces these days:
interface Two<T> { T make() }
interface One { <S> Two<S> do(S value) }
class MyBuilder<T> implements One, Two<T> {
public static One newInstance() {
return new MyBuilder<Object>(null);
}
private T value; // private constructors omitted
public <S> Two<S> do(S value) {
return new MyBuilder<S>(value);
}
public T make() {
return value;
}
}
You can, of course, create smarter constructions where you avoid the unused fields. If you want to look at examples of me using this approach, look at my two projects which use fluent interfaces quite heavily:
Byte Buddy: API for defining a Java class at run time.
PDF converter: A conversion software for converting files using MS Word from Java.

This is not a solution for your original problem. It is only an attempt to capture your actual intention, and sketch an approach where where the original problem does not appear. (I like generics - but class names like CommandMessage<T extends CommandMessage<T>> extends Message<CommandMessage<T>> make me shudder...)
I know that this is structurally rather different from what you originally asked about, and you might have omitted some details in the question that narrow down the range of possible answers so that the following is no longer applicable.
But if I understood your intention correctly, you could consider letting the subtypes be handled by the fluent calls.
The idea here is that you initially can only create a simple Message:
Message m0 = Message.newMessage();
Message m1 = m0.withID("id");
On this message, you can call the withID method - that's the only method that all messages have in common. The withID method returns a Message in this case.
Until now, the message is neither a CommandMessage nor any other specialized form. However, when you call the withCommand method, you obviously want to construct a CommandMessage - so you now simply return a CommandMessage:
CommandMessage m2 = m1.withCommand("command");
Similarly, when you call the withParameter method, you receive a CommandWithParamsMessage:
CommandWithParamsMessage m3 = m2.withParameter("name", "value");
This idea is roughly (!) inspired by a blog entry, which is in German, but the code nicely shows how this concept may be used to construct type-safe "Select-From-Where" queries.
Here, the approach is sketched, roughly adapted for your use-case. Of course, there are some details where the implementation will depend on how this is actually going to be used - but I hope that the idea becomes clear.
import java.util.HashMap;
import java.util.Map;
public class FluentTest
{
public static void main(String[] args)
{
CommandWithParamsMessage msg = Message.newMessage().
withID("do").
withCommand("doAction").
withParameter("arg", "value");
Message m0 = Message.newMessage();
Message m1 = m0.withID("id");
CommandMessage m2 = m1.withCommand("command");
CommandWithParamsMessage m3 = m2.withParameter("name", "value");
CommandWithParamsMessage m4 = m3.withCommand("otherCommand");
CommandWithParamsMessage m5 = m4.withID("otherID");
}
}
class Message
{
protected String id;
protected Map<String, String> contents;
static Message newMessage()
{
return new Message();
}
private Message()
{
contents = new HashMap<>();
}
protected Message(Map<String, String> contents)
{
this.contents = contents;
}
public Message withID(String id)
{
this.id = id;
return this;
}
public CommandMessage withCommand(String command)
{
Map<String, String> newContents = new HashMap<String, String>(contents);
newContents.put("command", command);
return new CommandMessage(newContents);
}
}
class CommandMessage extends Message
{
protected CommandMessage(Map<String, String> contents)
{
super(contents);
}
#Override
public CommandMessage withID(String id)
{
this.id = id;
return this;
}
public CommandWithParamsMessage withParameter(String paramName, String paramValue)
{
Map<String, String> newContents = new HashMap<String, String>(contents);
newContents.put(paramName, paramValue);
return new CommandWithParamsMessage(newContents);
}
}
class CommandWithParamsMessage extends CommandMessage
{
protected CommandWithParamsMessage(Map<String, String> contents)
{
super(contents);
}
#Override
public CommandWithParamsMessage withID(String id)
{
this.id = id;
return this;
}
#Override
public CommandWithParamsMessage withCommand(String command)
{
this.contents.put("command", command);
return this;
}
}

Related

Java generics wildcard for different subtypes clarification

I want to avoid explicit cast in my code, this is what I've done until now, but the compiler gives me some error and I don't understand how to fix it without cast.
I have a class with a map of listener, and some methods which notify the listeners:
public class MessageDispatcher {
private final Map<String, List<ControllerMessageWatcher<? extends ControllerMessage>>> watchers;
public void registerMessageWatcher(Class<? extends ControllerMessage> classToWatch, final ControllerMessageWatcher<?> controllerMessageWatcher) {
this.watchers.computeIfAbsent(classToWatch.getSimpleName(), k -> new ArrayList<>()).add(controllerMessageWatcher);
}
public synchronized void receive(StatusMessage statusMessage) {
List<ControllerMessageWatcher<?>> watchers = this.watchers.get(statusMessage.getClass().getSimpleName());
if (watchers != null)
watchers.forEach(controllerMessageWatcher -> controllerMessageWatcher.newMessage(statusMessage));
// Required type: capture of ? Provided: StatusMessage <-- error here! ^^^^
}
public synchronized void receive(UpdateDBMessage updateDBMessage) {
List<ControllerMessageWatcher<?>> watchers = this.watchers.get(updateDBMessage.getClass().getSimpleName());
if (watchers != null)
watchers.forEach(controllerMessageWatcher -> controllerMessageWatcher.newMessage(updateDBMessage));
// Required type: capture of ? Provided: UpdateDBMessage <-- error here! ^^^^
}
}
This is the interface for the listeners:
public interface ControllerMessageWatcher<T extends ControllerMessage> {
void newMessage(T message);
}
And this is the implementation of listener that I wish use avoiding casting:
public class Status implements ControllerMessageWatcher<StatusMessage> {
#Override
public void newMessage(StatusMessage message) {
...
}
}
All works except the methods receive which gives errors (or unchecked warnings if I remove wildcards).
What I want to achieve is avoid explicit cast in every newMessage methods (I have to do this if I remove all generics part).
And avoiding errors/warnings when I call newMessage from MessageDispatcher.receive() methods (what I have now) (see comments in that methods)
This are ControllerMessage, StatusMessage and UpdateDBMessage:
public abstract class ControllerMessage {
private String messageId;
// ... getter setter
}
public class StatusMessage extends ControllerMessage{
private String aField;
// ... getter setter
}
public class UpdateDBMessage extends ControllerMessage{
private String aField;
// ... getter setter
}
I think your design is fundamentally broken because of your use of reflection.
public void registerMessageWatcher(
Class<? extends ControllerMessage> classToWatch,
final ControllerMessageWatcher<?> controllerMessageWatcher
)
This step, at the level of your API, you have made it impossible for the compiler to manage your inheritance.
A small improvement would be to make the type consistent.
public <T extends ControllerMessage> void registerMessageWatcher(
Class<T> classToWatch,
final ControllerMessageWatcher<T> controllerMessageWatcher
)
Now we are keeping track of our class type, for better or worse, but we still need to caste to keep the classes in the map.
private final Map<Class<?>, List<ControllerMessageWatcher<?>>> watchers;
List<ControllerMessageWatcher<T>> getWatchers( Class<T> clazz ){
return (List<ControllerMessageWatcher<T>>)watchers.get(clazz);
}
To avoid casting I would make
public interface ControllerMessageWatcher<T extends ControllerMessage> {
void newMessage(ControllerMessage message);
}
I don't know what you're doing with your watcher that would new message to distinguish the type. This way you can have.
List<ControllerMessageWatcher<? extends ControllerMessage>> watchers;
watchers.forEach( w -> w.newMessage( message ) );
Then replace the abstract classes with interfaces.
interface ControllerMessage{
String getMessageId();
String getAField();
}
Now it is up to the specific watchers to grab the relevant information. You can also make default methods for fields that are not relevant.

Java: Instantiate a generic type by returning a Supplier vs returning a new instance

I was reading how to instantiate a generic and after reading and applying this answer; I would like to know what would be the differences between expecting a Supplier<T> vs. expecting a new instance of T.
Example:
abstract class AbstractService<T extends AbstractEntity> {
protected Supplier<T> makeNewThing(); // supplier is expected
public T myMethod(){
T object = makeNewThing().get(); // local object by calling supplier
object.doStuff();
return object;
}
}
class CarService extends AbstractService<Car> {
public Supplier<Car> makeNewThing(){
return Car::new;
}
}
vs.
abstract class AbstractService<T extends SomeAbstractEntity> {
protected T makeNewThing(); // object is expected, newness is assumed
public T myMethod(){
T object = makeNewThing(); // local object by calling constructor
object.doStuff();
return object;
}
}
class CarService extends AbstractService<Car> {
public Car makeNewThing(){
return new Car();
}
}
The only thing I can think of is that expecting a supplier ensures that a new object will be created, but when expecting an object we can only assume that the implementing classes are calling the constructor and not re-using an existing instance.
I'd like to know of other objective differences and possible use cases, if any. Thanks in advance.
Using a Supplier postpones the creation of the instance.
This means that you might avoid a creation of an unnecessary instance.
For example, suppose you pass the output of makeNewThing() to some method.
public void makeNewThingSometimes (T newInstance)
{
if (someCondition) {
this.instance = newInstance;
}
}
public void makeNewThingSometimes (Supplier<T> supplier)
{
if (someCondition) {
this.instance = supplier.get();
}
}
Calling the first variant requires creating an instance of T even if you are not going to use it.
Calling the second variant only creates an instance of T when necessary.
Using a Consumer can save both storage (if the create instance requires a significant amount of memory) and time (if the execution of the constructor is expansive).
The only thing I can think of is that expecting a supplier ensures
that a new object will be created,
Not necessarily.
You implement the Supplier in this way :
return SomeEntityImplementation::new;
But you could have implemented it in this other way :
if (myCachedObject != null){
return (()-> myCachedObject);
}
return SomeEntityImplementation::new;
Both ways may be used to return a cached object or create a new one.
One of Supplier advantages is the case of Supplier creating an object : this one is actually created only as the Supplier.get() method is invoked.
Note that in your example, using Supplier doesn't bring any advantage as in both cases (with or without Supplier) the object creation is already performed in a lazy way : as the factory method is invoked.
To take advantage of it, you should have a method that provides a Supplier<T> as parameter as in the Eran and Dasblinkenlight examples.
Another Supplier advantage is its ability to implement factory that may return multiple of things.
Using Supplier allows to have a shorter and more readable code and besides that doesn't rely on Java Reflection.
Supposing that you want to create the object from an Enum value, you could so write :
public enum MyBaseClassFactory {
ENUM_A (A::new),
ENUM_B (B::new),
ENUM_C (C::new),
ENUM_D (D::new);
private Supplier<BaseClass> supplier;
MyBaseClassFactory (Supplier<BaseClass> supplier){
this.supplier = supplier;
}
public BaseClass createObject(){
return supplier.get();
}
}
You could so use it :
BaseClass base = MyBaseClassFactory.ENUM_A.createObject();
Without Supplier, you will have to use Reflection (that may fail at runtime) or write a verbose and unmaintainable code.
For example with Reflection :
public enum MyEnumFactoryClass {
ENUM_A(A.class), ENUM_B(B.class), ENUM_C(C.class), ENUM_D(D.class);
private Class<BaseClass> clazz;
MyEnumFactoryClass(Class<BaseClass> clazz) {
this.clazz = clazz;
}
public BaseClass createObject() {
return clazz.newInstance();
}
}
For example without reflection but with more verbose code :
public enum MyEnumFactoryClass {
ENUM_A {
#Override
public BaseClass createObject() {
return new A();
}
},
ENUM_B {
#Override
public BaseClass createObject() {
return new B();
}
},
ENUM_C {
#Override
public BaseClass createObject() {
return new C();
}
},
ENUM_D {
#Override
public BaseClass createObject() {
return new D();
}
};
public abstract BaseClass createObject();
}
You could of course take advantage in a close way of Supplier by using it with a Map<String, Supplier<BaseClass>>.
The first solution is more flexible, because an extra level of indirection in object creation lets users of your class library change the source of new items independently of ServiceImpl<SomeEntityImplementation> class.
You can make a new Supplier<T> instance without subclassing or recompiling ServiceImpl, because there is an extra level of indirection. ServiceImpl could be implemented as follows:
class ServiceImpl<SomeEntityImplementation> {
private final Supplier<SomeEntityImplementation> supplier;
public Supplier<T> makeNewThing(){
return supplier;
}
public ServiceImpl(Supplier<SomeEntityImplementation> s) {
supplier = s;
}
}
This makes it possible for users of ServiceImpl to provide their own Supplier<T>, which is not possible using the second approach, in which the source of new items is merged into the implementation of service itself.

Java - Handling generics with inheritance

I have a class that represents a vendor service and all their services have an authentication and an execute method.
I started thiking of an abstract class that represents this as below.
The thing is each of their services require a different request object, so I thought of using generics.
The problem is that if use it, I can't handle the specifics of each request object. Each children must use some methods from the type.
1) Should I try to make this way I'm trying, or remove this executeRequest method from the abstract class and each subclass implement it with the correct type?
2) I always hear "prefer composition over inheritance". Should I move the executeRequest to an interface?
Thanks in advance!
public abstract class VendorService {
private final VendorInitialization VendorInitialization;
//a bean with some auth params
public VendorService(VendorInitialization VendorInitialization) {
this.VendorInitialization = VendorInitialization;
}
protected abstract <T> boolean validateRequest(T requestObject) throws VendorServiceBadRequest;
protected abstract <T, P> P executeRequest(T requestObject);
}
public class VendorServiceAllocation extends VendorService {
public VendorServiceAllocation(VendorInitialization VendorInitialization) {
super(VendorInitialization);
}
#Override
protected <T> boolean validateRequest(T requestObject) throws VendorServiceBadRequest {
//List<BeanAllocation> requestObject = new Arraylist<>(); //I was using like this before
//TODO: how to handle it as list of on this specific case?
if (requestObject == null || requestObject.size() == 0) {
throw new VendorServiceBadRequest(String.format("The list must have at least one element"));
}
//TODO: requestObject.get(0).getMySpecificFieldFromBeanAllocation will not work
//some checks
return true;
}
#Override
protected <T, P> P executeRequest(T requestObject) {
//executes and return a list of objects specific to this class
return new List<BeanAllocationResponse>();
}
}
Edit, for clarification:
In the child class VendorServiceAllocation, I need to use some methods that are specific of that type.
E.g.: Inside executeRequest, I need to call requestObject.customFunctionFromChild()
I think niceman hit the nail on the head, though I am not quite sure what you are asking. eg.
abstract class Service<T,P>{
abstract public P processRequest(T t);
}
Then you can implement it in one of two ways.
class StringService extends Service<String, String>{
public String processRequest(String t){
return t;
}
}
Or you could leave it to still be Generic and the actual instances would have the different types.
class OtherService<T> extends Service<T, String>{
public String processRequest(T t){
return t.toString();
}
}
Where you could use it as,
OtherService<Integer> is = new OtherService<>();

Using Enum for factory in Java, a best practice?

Java allow us to embed data and behaviour on Enum.
I don't want to implement a factory directly on an Enum, because I think this is not its role.
But I can put class reference on the enum, and contruct object on an external factory. Comparing to a traditionnal factory pattern, what is the best implementation for you ? Which solution is better to use in which case ?
Now, the code.
Function used in both solutions to construct objects. Usefull to implement fly-weight pattern with a Map if required.
private Action getAction(Class<? extends Action> actionClazz) {
// logger + error handling
return actionClazz.newInstance();
}
1) With a traditionnal factory:
public enum ActionEnum {
LOAD_DATA,
LOAD_CONFIG;
}
public Action getAction(ActionEnum action) {
switch (action) {
case LOAD_CONFIG:
return getAction(ActionLoadConfig.class);
case LOAD_DATA:
return getAction(ActionLoadData.class);
}
}
2) With Enum-styled factory :
public enum ActionEnum {
LOAD_DATA(ActionLoadConfig.class),
LOAD_CONFIG(ActionLoadData.class);
public ActionEnum(Class<? extends Action> clazz){...}
public Class<? extends Action> getClazz() {return this.clazz}
}
public Action getAction(ActionEnum action) {
return getAction(action.getClazz());
}
IMO calling newInstance() should be avoided if at all possible, as it blatantly defeats some of the compile time protection given by java (read its javadoc) and introduces new Exceptions to handle.
Here's a solution similar to what Sergey provided, just a little more concise thanks to functional interfaces and method references.
public enum ActionEnum {
LOAD_DATA(ActionLoadData::new),
LOAD_CONFIG(ActionLoadConfig::new)
private Supplier<Action> instantiator;
public Action getInstance() {
return instantiator.get();
}
ActionEnum(Supplier<Action> instantiator) {
this.instantiator = instantiator;
}
}
public Action getAction(ActionEnum action) {
return action.getInstance();
}
The second one is much cleaner: it doesn't need any long switch block, and has 0 risk of forgetting one of the enum values like the first one has.
It's not always possible to use it, though, because the enum might be some generic enum (Month, for example), that should not be coupled to the factory of actions.
This works for me:
enum ActionEnum
{
LOAD_DATA {
#Override
public ActionLoadData getInstance() {
return new ActionLoadData ();
}
},
LOAD_CONFIG {
#Override
public ActionLoadConfig getInstance() {
return new ActionLoadConfig();
}
};
public abstract ILightBulb getInstance();
}
class ActionFactory
{
public Action getAction(ActionEnum action)
{
return action.getInstance();
}
}
To decouple even more:
static final EnumMap<ActionEnum, Class<? extends Action>> enumToClass = new EnumMap<>();
static
{
enumToClass.put(ActionEnum.LOAD_DATA, ActionLoadData.class);
etc...
}
public Action getAction(ActionEnum action)
{
return getAction(enumToClass.get(action));
}
EnumMap is very fast so no worries.

Java generics (template) specialization possible (overriding template types with specific types)

I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.
In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.
Merely providing a type-specific overload of a method produces a "method is ambiguous" error:
e.g.:
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Is the only option to subclass the generic class with a specific type (see StringBar in the following example?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.
I'm guessing I could check the instanceof in the Bar.get() method, e.g.
T get(T t) {
if (t instanceof String) return "";
if (t instanceof Integer) return 0;
else return null;
}
However I've been taught to avoid instanceof and use polymorphism when possible.
All things considered, the concensus appears to be that the StringBar method mentioned in the question is the only way to go.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".
BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the class of the object to be processed:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.
The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.
See an explanation by Jon Skeet's:
What is the concept of erasure of generics in java?
Java Generics - Types erasures - when and what happens?
You can subclass Bar<T> and create StringBar (note I removed the static keyword) and override get() method.
public class BarString extends Bar<String> {
#Override
public String get(String x) {
return "";
}
}
Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.
Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
I don't see why you need to specialize for a String here.

Categories