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

4
我正在尝试定义一个类(或一组实现相同接口的类),使其表现为一个松散类型的对象(类似于JavaScript)。它们可以持有任何类型的数据,对它们的操作取决于底层类型。
我已经用三种不同的方式实现了它,但都不是理想的。这些测试版本只允许字符串和整数,并且唯一的操作是添加。将整数相加会得到整数值的总和,将字符串连接起来,将整数与字符串相加会将整数转换为字符串并将其与字符串连接起来。最终版本将拥有更多类型(双精度浮点数、数组、类似于JavaScript的对象,其中可以动态添加新属性)和更多操作。
第一种方式:
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));
  }
}

另一种方式: public interface DynObject2 { @Override public String toString(); public DynObject2 add(DynObject2 d); }

DynString2的实现方式与DynString1类似。

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也是类似的

方法三:

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不允许switch使用Classes。

无论如何...我的问题是,处理这样的事情最好的方法是什么?


1
顺便提一下,如果我的Java知识没有出错的话,Class c = d.getClass(); if(c==DynInteger2.class) 可以写成 if (d instanceof DynInteger2) - Thomas
2个回答

2
您正在尝试做的是双重分派。您希望调用的方法既依赖于它被调用的对象的运行时类型,也依赖于它的参数的运行时类型。
Java和其他C衍生语言仅支持单一分派,这就是为什么您需要像选项1中使用的访问者模式这样的把戏。这是实现它的常见方式。我更喜欢这种方法,因为它不使用反射。此外,它允许您将每个情况保留在自己的方法中,而无需一个大的“交换机”方法来进行分派。

0
我会选择第二个选项,对于第三个选项,最好使用泛型,这样你就不必依赖于那个枚举。而对于第一个选项,你可能要实现一辈子的方法。无论如何,你可以使用“instanceof”运算符进行类匹配。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接