Is there a way to avoid instanceof 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 the end of the process group the messages with different "types". Something like this:

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 {};

      

etc. (I am skipping constructors in derived classes for clarity). I can distinguish the List using the operator the instanceof , , but . I think generics will be more graceful, but then how can I distinguish between them in the list?

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

      

I was thinking about using enums

enum MessagType { ERROR, FILE_ERROR, OK }

      

for different classes, but I couldn't come up with a solution. Thank.

+3


source to share


2 answers


What you can usually do in this situation is to force the Message to have a function that can be overridden by subclasses. Then each subclass can have its own behavior when this function is called, or it can retain the default behavior for messages. If the default doesn't make sense, you can make Message an abstract class.

Then you can loop over the 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 nested loop through messagesByType

to process each other type one at a time. That being said, I don't really see the need to ever do this given the original answer, but it should answer your question.

+3


source


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

So, I'll just have the post type as a class attribute Message

:

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 have to keep it simple, see the KISS principle .



EDIT:

If you want to group posts by type, you can do it like this:

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

      

messages

- this List

containing all messages.

+2


source







All Articles