bretter
bretter

Reputation: 451

Is there any way to avoid instanceof by using generics?

I have a process where multiple places can generate error messages and success messages, so I would like to track them all with one class and then at end of the process, group the messages by the different 'types'. Something along these lines:

public class Message {
  String message;
  public Message(String message) {
      this.message = message;
  }
}

public class ErrorMessage extends Message {};
public class FileErrorMessage extends ErrorMessage {};
public class OkMessage extends Message {};

and so on. (I'm skipping constructors in derived classes for clarity). I can discriminate among the List by using the instanceof operator, but I think generics would be more elegant, but then how do I discriminate among them in a List ?

Message<ErrorMessage> eMsg = new Message<ErrorMessage>("invalid user");
Message<FileErrorMessage> feMsg = new Message<FileErrorMessage>("file not found");

I thought of using enums

enum MessagType { ERROR, FILE_ERROR, OK }

for the different classes but I could not come up with a solution. Thanks.

Upvotes: 3

Views: 491

Answers (2)

muzzlator
muzzlator

Reputation: 742

What you can typically do in this situation is make Message have a function that can be overridden by the subclasses. Then each subclass can have its own behavior when that function is called, or it can retain the default behavior for Messages. If no default makes sense, you can make Message an abstract class.

Then you can loop through lists and do things like:

for (Message m : messages) {
    m.function();
}

Hope this is what you were looking for!

Edit: In response to your comment below, you can do something like this (with guava):

SetMultimap<Class, Message> messagesByType = HashMultimap.create();
for (Message m : messages) {
    messagesByType.add(m.getClass(), m);
}

Then you can do a nested loop through messagesByType to handle each different type one at a time. That being said, I don't really see the need to ever do this, given the initial response of mine, but this is to answer your question.

Upvotes: 3

fps
fps

Reputation: 34460

Using generics in this case makes no sense to me. Using inheritance to discriminate by the type of message woud make sense if different types of message had different behavior, but this doesn't seem to be the case.

So, I would just have the type of message as an attribute of the Message class:

enum MessageType { ERROR, FILE_ERROR, OK }

public class Message {

    private final String message;

    private final MessageType type;

    public Message(String message, MessageType type) {
        this.message = message;
        this.type = type;
    }

    public MessageType getType() {
        return this.type;
    }

    // getter for message
}

In general, you should keep it simple, see the KISS principle.

EDIT:

If you want to group messages by type, you can do it as follows:

Map<MessageType, Message> messagesByType = messages.stream()
    .collect(Collectors.groupingBy(Message::getType));

messages is a List containing all the messages.

Upvotes: 2

Related Questions