Java Class.cast () and Overloading

I am trying to code a packet listener for a small server. I am very new to Java, and this is the first time I've been talking to the network. The whole idea that it receives a package corresponds to the identifier of the package with its class, passes the input stream to the package constructor so that it can be constructed, and then pass it to the packet Hander, which will have an overloaded method for each package. To achieve this, use an array that maps the packet identifiers to the classes of each and uses a method called decoding to build the packet. The problem is that handlePacket overloading does not behave as expected. Let's look at the code.

I have a packet listener in the stream, and the execution method looks like this:

public void run() { try { int packet_id; while ((packet_id = istream.readInt()) != -1) { plugin.getServer().getConsoleSender().sendMessage("[Comm] Recived packet " + packet_id); Packet packet = decode(packet_id, istream); plugin.getServer().getConsoleSender().sendMessage("[Comm] Packet is " + Class.forName(packet.getClass().getName())); plugin.getServer().getConsoleSender().sendMessage("[Comm] Class is " + Packet00ReqIdentify.class.cast(packet).getClass().getName()); plugin.getServer().getConsoleSender().sendMessage("[Comm] Class is " + Class.forName(packet.getClass().getName()).getName()); handlePacket(packet); } } catch (IOException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { e.printStackTrace(); } } 

The decode and handlePacket methods are as follows:

 private void handlePacket(Packet00ReqIdentify packet) throws IOException { plugin.getServer().getConsoleSender().sendMessage("[Comm] Got it!"); } private void handlePacket(Packet packet) { plugin.getServer().getConsoleSender().sendMessage("[Comm] Woops!"); } private Packet decode(int packet_id, PacketInputStream istream) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IOException { Class<? extends Packet> packet_class = packets_ids.get(packet_id); try { Constructor<?> packet_constructor = packet_class.getConstructor(PacketInputStream.class); return Class.forName(packet_class.getName()).asSubclass(Packet.class).cast(packet_constructor.newInstance(istream)); } catch (NoSuchMethodException e) { return Class.forName(packet_class.getName()).asSubclass(Packet.class).cast(packet_class.newInstance()); } } 

packets_ids is an array containing a reference to the class of each packet, indexed by their identifiers:

 private static ArrayList<Class<? extends Packet>> packets_ids; 

It is initialized as follows:

 private static void registerPacket(int id, Class<? extends Packet> oclass) { packets_ids.add(id, oclass); } static { packets_ids = new ArrayList<Class<? extends Packet>>(); registerPacket(Packet00ReqIdentify.assigned_pid, Packet00ReqIdentify.class); registerPacket(Packet01Identify.assigned_pid, Packet01Identify.class); registerPacket(Packet02Heartbeat.assigned_pid, Packet02Heartbeat.class); } 

If I performed this and tested it by sending a packet of type 00, I get the following:

 17:37:49 [INFO] [Comm] Connection established to localhost:11000 17:37:49 [INFO] [Comm] Recived packet 0 17:37:49 [INFO] [Comm] Packet is class com.gamerarg.commclient.protocol.Packet00ReqIdentify 17:37:49 [INFO] [Comm] Class is com.gamerarg.commclient.protocol.Packet00ReqIdentify 17:37:49 [INFO] [Comm] Class is com.gamerarg.commclient.protocol.Packet00ReqIdentify 17:37:49 [INFO] [Comm] Woops! 

So this means that packet00 was not bound by "handlePacket (Packet00ReqIdentify packet)". If I do an explicit cast to a “package” in a call to handlePacket, this works. So the questions are:

  • Why is this not working? When I print class names for both, I get the same thing.

  • How can I make it work? I struggled with this for 6 or 7 hours, read, searched, tried, and saw code from others. One simple solution is to make a switch using a packet identifier, but I want something more elegant. Maybye, I am mistaken from the basic idea, therefore, why I sent the code, I am open to suggestions and ideas from more experienced people in the subject, including recommendations for material in the subject.

Thanks!

+3
java casting class overloading double-dispatch
source share
3 answers

In each of the Packet subclasses, we implement the public void handle() method, which does what you need to process the package. Or

  • Set the default implementation of handle() to Packet , or
  • Declare handle() as abstract in Packet and make Packet abstract class or
  • Declare handle() in Packet and make the Packet interface.

Then replace

 handlePacket(packet); 

from

 packet.handle(); 

This is polymorphism in action. It will work at runtime, checking the class of the object that references the Packet , and invoking the correct version of the handle method.

If handle() needs access to the original PacketListener , then declare it as a public void handle(PacketListener listener) and name it as packet.handle(this);

+3
source share

Here is your problem:

 Packet packet = decode(packet_id, istream); --snip-- handlePacket(packet); 

Since packet defined as packet , it is routed to the handlePacket(Packet packet) method, although the execution type is a subclass of Packet.

+2
source share

You can do it (java8 required)

 static Map<Class<?>, Consumer<?>> handlers = new HashMap<>(); void handlePacket(Packet packet) { Consumer<Packet> handler = (Consumer<Packet>)handlers.get(packet.getClass()); handler.accept(packet); } static { handlers.put(Packet00ReqIdentify.class, (Packet00ReqIdentify packet)->{ System.out.println("Packet00ReqIdentify"); }); handlers.put(Packet01Identify.class, (Packet01Identify packet)->{ System.out.println("Packet01Identify"); }); // etc. } 

This use case of "double sending" is quite common, we must make a general utility for it, for example

 public class DoubleDispatch<T, R> { public R invoke(T obj){...} public <C extends T> void register(Class<C> type, Function<C,R> func){...} } 

What can be used to solve this problem:

  DoubleDispatch<Packet,Void> dd = new DoubleDispatch<>(); dd.register(Packet00ReqIdentify.class, packet->{ System.out.println("Packet00ReqIdentify"); return null; }); // etc ... dd.invoke(packet); 

Without a lambda expression, we could use an anonymous inner class to achieve something like

  dd.new Handler<Packet00ReqIdentify>(){ public Void handle(Packet00ReqIdentify obj) { System.out.println("Packet00ReqIdentify"); return null; } }; 
+2
source share

All Articles