Java Best Practices for Runtime Type Resolution

I am trying to define a class (or a set of classes implementing the same interface) that will behave like a freely typed object (like JavaScript). They can store any data, and operations with them depend on the base type.

It works for me differently, but no one seems perfect. These test versions only allow strings and integers, and a single operation is added. Adding integers results in a sum of integers, adding strings combines the strings and adding an integer to a string converts the integer to a string and combines it with a string. The final version will have more types (doubles, arrays, objects similar to JavaScript, where new properties can be added dynamically) and more operations.

Method 1:

public interface DynObject1 { @Override public String toString(); public DynObject1 add(DynObject1 d); public DynObject1 addTo(DynInteger1 d); public DynObject1 addTo(DynString1 d); } public class DynInteger1 implements DynObject1 { private int value; public DynInteger1(int v) { value = v; } @Override public String toString() { return Integer.toString(value); } public DynObject1 add(DynObject1 d) { return d.addTo(this); } public DynObject1 addTo(DynInteger1 d) { return new DynInteger1(d.value + value); } public DynObject1 addTo(DynString1 d) { return new DynString1(d.toString()+Integer.toString(value)); } } 

... and similar for DynString1

Path 2: public interface DynObject2 {@Override public String toString (); public DynObject2 add (DynObject2 d); }

 public class DynInteger2 implements DynObject2 { private int value; public DynInteger2(int v) { value = v; } @Override public String toString() { return Integer.toString(value); } public DynObject2 add(DynObject2 d) { Class c = d.getClass(); if(c==DynInteger2.class) { return new DynInteger2(value + ((DynInteger2)d).value); } else { return new DynString2(toString() + d.toString()); } } } 

... and similar for DynString2

Method 3:

 public class DynObject3 { private enum ObjectType { Integer, String }; Object value; ObjectType type; public DynObject3(Integer v) { value = v; type = ObjectType.Integer; } public DynObject3(String v) { value = v; type = ObjectType.String; } @Override public String toString() { return value.toString(); } public DynObject3 add(DynObject3 d) { if(type==ObjectType.Integer && d.type==ObjectType.Integer) { return new DynObject3(Integer.valueOf(((Integer)value).intValue()+((Integer)value).intValue())); } else { return new DynObject3(value.toString()+d.value.toString()); } } } 

With if-else logic, I could use value.getClass () == Integer.class instead of saving the type, but with a lot of types I would change this to use the switch statement, and Java doesn't allow the use of the Class switch.

Anyway ... My question is the best way to do something like this?

+4
source share
2 answers

What you are trying to do is called double dispatch . You want the called method to depend on both the type of runtime of the called object and the type of its argument.

Java and other C derivatives support only one send, so you need kludge as the visitor template that you used in option 1. This is a general way to implement it. I would prefer this method because it does not use any reflection. In addition, it allows you to store each case in its own method, without requiring a large โ€œswitchโ€ to perform scheduling.

+2
source

I would choose the second option, with the third, I better use generics so that you do not rely on this Enum. And with the first option, you could implement the methods for the rest of your life. In either case, you can use the instanceof operator to map classes.

0
source

Source: https://habr.com/ru/post/1309081/


All Articles