运行时类型解析的 Java 最佳实践

发布于 2024-08-31 19:59:34 字数 2503 浏览 1 评论 0原文

我试图定义一个类(或一组实现相同接口的类),它将充当松散类型对象(如 JavaScript)。它们可以保存任何类型的数据,并且根据底层类型对它们进行操作。

我让它以三种不同的方式工作,但似乎没有一种是理想的。这些测试版本仅允许字符串和整数,并且唯一的操作是加法。添加整数会得到整数值的总和,添加字符串会连接字符串,而将整数添加到字符串会将整数转换为字符串并将其与字符串连接。最终版本将有更多类型(双精度数、数组、类似 JavaScript 的对象,可以动态添加新属性)和更多操作。

方式 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));
  }
}

...与 DynString1

方式 2 类似: 公共接口 DynObject2 { @Override public String toString(); 公共 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());
    }
  }
}

类似

与 DynString2方式 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());
    }
  }
}

:使用 if-else 逻辑,我可以使用 value.getClass()==Integer.class 而不是存储类型,但对于更多类型,我将更改它以使用 switch 语句和Java 不允许切换使用类。

无论如何......我的问题是处理此类事情的最佳方法是什么?

I'm trying to define a class (or set of classes which implement the same interface) that will behave as a loosely typed object (like JavaScript). They can hold any sort of data and operations on them depend on the underlying type.

I have it working in three different ways but none seem ideal. These test versions only allow strings and integers and the only operation is add. Adding integers results in the sum of the integer values, adding strings concatenates the strings and adding an integer to a string converts the integer to a string and concatenates it with the string. The final version will have more types (Doubles, Arrays, JavaScript-like objects where new properties can be added dynamically) and more operations.

Way 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

Way 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

Way 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 the if-else logic I could use value.getClass()==Integer.class instead of storing the type but with more types I'd change this to use a switch statement and Java doesn't allow switch to use Classes.

Anyway... My question is what is the best way to go about something thike this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

许仙没带伞 2024-09-07 19:59:34

您想要做的事情称为双重调度。您希望调用的方法既取决于调用它的对象的运行时类型,又取决于其参数的运行时类型。

Java 和其他 C 衍生产品仅支持单分派,这就是为什么您需要像 访问者模式 您在选项 1 中使用过。这是实现它的常见方法。我更喜欢这种方法,因为它不使用反射。此外,它允许您将每个案例保留在自己的方法中,而不需要一个大的“总机”方法来进行调度。

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

Java and other C derivatives support single dispatch only, which is why you need a kludge like the visitor pattern you used in option 1. This is the common way of implementing it. I would prefer this method because it uses no reflection. Furthermore, it allows you to keep each case in its own method, without needing a big "switchboard" method to do the dispatching.

千鲤 2024-09-07 19:59:34

我会选择第二个选项,对于第三个选项,我最好使用泛型,这样您就不会依赖于该枚举。通过第一种选择,您可以在余生中实施方法。无论如何,您可以使用“instanceof”运算符进行类匹配。

I'd choose the second option, with the third, I'd better be using generics so you don't rely on that Enum. And with the first option you could be implementing methods for the rest of your life. Anyways you could use "instanceof" operator for Class matching.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文