3

I have these classes:

My problem is that for each class I am having to manually register the node.

/**
 * Contains the encoder for messages from the server.
 * 
 * @since 18/08/2018
 */
public abstract class MessageEncoder<T> {

    /**
     * Register this encoder to a message.
     */
    public abstract void register();

    /**
     * Get the encoded message to send to the client.
     * 
     * @param message The message.
     * @return the {@link GamePacket} ready to be sent.
     */
    public abstract GamePacket encode(T message);

}

Where < T > is always someClass extends Message.

And here is how a message encoder looks like:

public final class ComponentMessageEncoder extends MessageEncoder<ComponentTextMessage> {

    @Override 
    public void register() {
        GameConstants.RELEASE.register(ComponentTextMessage.class, this);

    }

    @Override 
    public GamePacket encode(ComponentTextMessage message) {
        // TODO Auto-generated method stub
        return null;
    }

}

As you can see the items in the register method, I have to manually type that for every encoder I make.

Is there a shortcut for this that I can just put in MessageEncoder abstract class instead?

I could not find anything that works here

Edit:

Where register sig. is:

/**
     * The encoders.
     */
    private final Map<Class<? extends Message>, MessageEncoder<?>> encoders = new HashMap<>();

    /**
     * Register a encoder to a message.
     * 
     * @param message The message.
     * @param encoder The encoder.
     */
    public void register(Class<? extends Message> message, MessageEncoder<?> encoder) {
        encoders.put(message, encoder);
    }
kay
  • 451
  • 1
  • 3
  • 13
  • What's the signature of `GameConstants.RELEASE.register()`? – shmosel Aug 21 '18 at 21:50
  • It is populating a map < Class extends Message>, MessageEncoder > – kay Aug 21 '18 at 21:51
  • What's the method signature? – shmosel Aug 21 '18 at 21:51
  • What specific information are you after? what it returns, throws, parameters? – kay Aug 21 '18 at 21:53
  • 1
    You have to get `T.class`, when it is not possible directly, see https://stackoverflow.com/questions/3437897/ – Nikolay Aug 21 '18 at 21:53
  • All of the above. – shmosel Aug 21 '18 at 21:53
  • 1
    @shmosel edited op – kay Aug 21 '18 at 21:55
  • First of all, you can improve the method signature to only accept the right encoder type: ` void register(Class message, MessageEncoder encoder)`. Now, change the type parameter on the encoder class to `` and you should be able to use [this](https://stackoverflow.com/questions/18707582/get-actual-type-of-generic-type-argument-on-abstract-superclass) to extract the type of `T` in the superclass. – shmosel Aug 21 '18 at 22:17
  • Actually, once you do `` on the encoder class, you can just do `` on the register method. – shmosel Aug 21 '18 at 22:20
  • @shmosel can you provide an example? Kind of confused by what the classes should look like – kay Aug 21 '18 at 22:32
  • `register((Class extends Message>)((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0], this);` – shmosel Aug 21 '18 at 22:35
  • @kay You ask a lot of questions on here, but you have never **accepted** any of the answers. Why do you keep asking if you never get answers you like? --- Or is it that you don't know how this site works? If so, you should learn more in the [**Help Center**](https://stackoverflow.com/help), before asking more questions. – Andreas Aug 21 '18 at 23:39

1 Answers1

2

You can do slightly better with:

public abstract class MessageEncoder<T extends Message> {
    protected MessageEncoder(Class<? extends T> messageClass) {
      GameConstants.RELEASE.register(messageClass, this);
    }

    /**
     * Get the encoded message to send to the client.
     * 
     * @param message The message.
     * @return the {@link GamePacket} ready to be sent.
     */
    public abstract GamePacket encode(T message);
}

Now subclasses would do:

public final class ComponentMessageEncoder extends MessageEncoder<ComponentTextMessage> {
    ComponentMessageEncoder() {
      super(ComponentTextMessage.class);
    }

    // etc as before
}

This cuts down slightly on the repetition, and it allows the compiler to stop you from getting it wrong, so it's something of a win.

jacobm
  • 13,790
  • 1
  • 25
  • 27
  • 1
    I think the `ComponentMessageEncoder` constructor should pass to `super` a reference to `ComponentTextMessage.class` instead of `ComponentMessageEncoder.class`. Isn't it? – Little Santi Aug 21 '18 at 22:03
  • 1
    You need to make sure `T extends Message`. Also you need to pass the message type, not the encoder type. – shmosel Aug 21 '18 at 22:05