Java - 识别值类型的模式

时间:2012-02-14 11:48:42

标签: java object types casting

我正在为Java中的外部程序创建一个新的脚本生成器。这种语言支持变量,但它是一种无类型语言。这是我最初编写的代码示例:

public class Var
{
    private String name;
    private String type;
    private float defaultValue;
    private float lowerBound;
    private float upperBound;
    private float value;
    private LinkedList<Float> valuesConstraint;
    private String description;
    private Category category;
    private LinkedList<CvarDependency> dependencies;
    ...
}

通常var类型是Float,但它也可以是bool [0 | 1],String或int。 所以我最终做了这个实现:

abstract class Var
{
    private String name;
    ...
}

public class IntVar extends Var
{
    private int value;
    private int defaultValue;
    private int lowerBound; //-infinite
    private int upperbound; //+infinite
    ...
}

public class FloatVar extends Var
{
    private float value;
    private float defaultValue;
    private float lowerBound; //-infinite
    private float upperbound; //+infinite
    ...
}

public class StringVar extends Var
{
    private String value;
    private String defaultValue; //empty string
    ...
}

public class BoolVar extends Var
{
    private boolean value;
    private boolean defaultValue;
    private boolean lowerBound; //false <-> 0
    private boolean upperbound; //true  <-> 1
    ...
}

现在我必须将这些变量存储到LinkedList中,但是当我必须阅读其内容时,如何管理正确的强制转换?我已经读过使用这种方法不是一个好习惯:

Var var = Manager.getVar("namevar");
if( var.getClass().getName().equals("StringVar") )
    ...
else if( var.getClass().getName().equals("IntVar") )
    ...
else if( var.getClass().getName().equals("FloatVar") )
    ...
else if( var.getClass().getName().equals("BoolVar") )
    ...

有什么提示可以更好地解决这个问题吗?

4 个答案:

答案 0 :(得分:0)

不要比较班级名称,比较班级:

if( var.getClass().equals(IntVar.class) ) {
  IntVar intVar = (IntVar) var; 
  ...

您也可以使用var instanceof IntVar,但如果您编写一个扩展IntVar的类,则可能会出现问题。

答案 1 :(得分:0)

您可以使用通用类型,但我不知道您需要对成员做什么......

public class Var<T extends Comparable<T>>
{
    private String name;
    private String type;
    private T defaultValue;
    private T lowerBound;
    private T upperBound;
    private T value;
    private LinkedList<T> valuesConstraint;
    private String description;
    private Category category;
    private LinkedList<CvarDependency> dependencies;
    ...
}

答案 2 :(得分:0)

不是让你的解释器直接提取和操作值,也许最好采用不同的方法并将操作定义为这样的方法:

abstract class Var
{
    ...
    public Var add(Var var); // corresponds to    var + otherVar;   in script
    public Var subtract(Var var); // corresponds to    var - otherVar;   in script
    public Var unarySubtract(); // corresponds to    -var;   in script
    ...
}

这样你就可以通过调用这些方法来评估脚本中的表达式,并且你的类可以像这样重写和重载,例如:

public class IntVar extends Var
{
    ...
    public IntVar add(IntVar var)
    {
      return new IntVar(value + var.value); // add another int
    }
    public FloatVar add(FloatVar var)
    {
      return new FloatVar(value + var.value); // add another float (and cast result to float? up to you whether you want to do this...)
    }
    public StringVar add(StringVar var)
    {
      return new StringVar("" + value + var.value); // add a string, and cast to string (for string concatenation)
    }
    public Var add(Var var)
    {
      throw new OperationNotSupportedException(); // no other types can be added to an IntValue so throw an exception
    }
    public Var subtract(Var var) {...}
    public IntVar unarySubtract()
    {
      return new IntVar(-value);
    }
    ...
}

通过这种方式,您可以根据需要实现和评估它们的操作,而无需直接在解释器中处理这些值。

另一个好处是使用此方法,如果用户尝试运行错误的脚本(例如尝试将BoolVar添加到IntVar),则会在解释器中抛出异常。

也许您甚至可以在抽象Var类中使用默认的操作实现:

public Var add(Var var)
{
  throw new OperationNotSupportedException(); // no other types can be added to an IntValue so throw an exception
}

只有在安全使用操作时才覆盖/过载。

当你在这里说“阅读内容”时,可能至少需要在某个时刻以统一的方式处理某个类型实例的内容,如果这仅仅是为了输出,那么为什么不只是一个抽象的'toString()'方法?如果出于某些其他目的而不是输出,那么你有什么目的不能只使用其他一些toX()方法并以相同的方式处理每个类型实例?

我意识到这并没有直接回答你的问题,而是提出了一种不同的方法,希望完全避免这个问题,这可能完全没有用,因为我可能对你想要实现的目标做了一些愚蠢的假设。 o已经看到了问题的更多背景,但我希望它仍然有用。

如果这没有用,那么请提供更多信息,我会尝试多一点帮助,因为我不能建议继续当前的方法,但我不确定除此之外还有什么建议。

编辑:等等,我的坏,我没有正确地阅读这个问题。这不是解释器,而是生成器。什么样的发电机?什么语言的脚本生成器?那些,这些可能不是重要问题,但现在我已经重新阅读了这个问题,我意识到我对你需要实现的目标并不是很了解。

我仍然认为封装是比访问者模式更好的选择,一般来说:)。当然,除非有某些原因需要添加行为,这些行为可以对任何变量进行操作,使得行为无法通过Var提供的接口从已经附加到每个变量的行为构建...可能会有什么行为这是?对于输出,您肯定需要以所有这些变量共有的格式读取每个变量,为什么不仅仅是toString(),toSomething(),toAnotherThing()等。

答案 3 :(得分:0)

我的建议是使用Visitor模式。该算法将放在Visitor而不是对象Var(和子类)中。

public class Var {

    public void accept(VarVisitor visitor) {
        visitor.visit(this);
    }
}

访问者

public interface VarVisitor {
    public void visit(FloatVar var);
    public void visit(IntVar var);
    public void visit(StringVar var);
    public void visit(BoolVar var);

    //...etc.
    public Object getValue();
}

VisitorImpl

public class VarVisitorImpl implements VarVisitor {
    private Object value;

    @Override
    public Object getValue() {
        return value;
    }

    @Override
    public void visit(FloatVar var) {

    }

    @Override
    public void visit(IntVar var) {

    }

    @Override
    public void visit(StringVar var) {

    }

    @Override
    public void visit(BoolVar var) {

    }
}

我希望这可以让您了解自己想要达到的目标。