在运行时

时间:2018-05-16 15:13:35

标签: java generics casting

我们被赋予了创建用于评估表达式的对象结构的任务(例如“1 + 2”或“true& false”)。提供了解析器,以及用于创建表达式对象的工厂接口。

我们对不同的表达式使用泛型:Expression<Integer>返回一个整数,Expression<Boolean>返回一个布尔值等。

我们面临的问题是提供的界面使用原始类型Expression,例如:

public Expression createSumExpression(Expression left, Expression right);

仅在操作数类型为Expression<Integer>Expression<Double>时定义sum表达式。我们怎么检查这个? 由于类型擦除,类型信息在运行时不可用,如果它不正确,它只会导致ClassCastException

我可以修改所有内容,createSumExpressioncreateAndExpression函数除外。

这是一段简化的代码,用于演示此问题。它可以工作,但它看起来不太好,它会发出多个警告。

Main.java

public class Main {

    public static void main(String[] args) {
        Expression<?> left1 = new BasicExpression<>(42);
        Expression<?> right1 = new BasicExpression<>(3);        
        Expression<?> sum = createSumExpression(left1, right1);
        System.out.printf("%d + %d = %d%n",left1.getValue(), right1.getValue(), sum.getValue());

        Expression<?> left2 = new BasicExpression<>(true);
        Expression<?> right2 = new BasicExpression<>(false);
        Expression<?> and = createAndExpression(left2, right2);
        System.out.printf("%b & %b = %b%n",left2.getValue(), right2.getValue(), and.getValue());
    }

    private static Expression createSumExpression(Expression left, Expression right) { // Raw types because of given interface
        return new BinaryExpression<Integer,Expression<Integer>,Expression<Integer>>(left, right) {
            @Override
            protected Integer operation(Expression<Integer> left, Expression<Integer> right) {
                return left.getValue() + right.getValue();
            }
        };
    }

    private static Expression createAndExpression(Expression left, Expression right) { // Raw types because of given interface
        return new BinaryExpression<Boolean,Expression<Boolean>,Expression<Boolean>>(left, right) {
            @Override
            protected Boolean operation(Expression<Boolean> left, Expression<Boolean> right) {
                return left.getValue() & right.getValue();
            }
        };
    }

}

Expression.java

abstract public class Expression<V> {
    public abstract V getValue();
}

BasicExpression.java

public class BasicExpression<V> extends Expression<V> {
    public BasicExpression(V value) {
        this.value = value; 
    }
    @Override
    public V getValue() {
        return value;
    }
    private V value;
}

BinaryExpression.java

abstract public class BinaryExpression<V, L, R> extends Expression<V> {
    public BinaryExpression (L l, R r) {
        this.left = l;
        this.right = r;
    }
    @Override
    public V getValue() {
        return operation(left, right);
    }
    abstract protected V operation(L left, R right);

    private L left;
    private R right;
}

提供的界面是:

/**
 * @param <E>
 *            Your class for representing an expression.
 */
public interface IExpressionFactory<E> {
    public E createSumExpression(E left, E right) throws ModelException;
    public E createAndExpression(E left, E right) throws ModelException;
    // ...
}

3 个答案:

答案 0 :(得分:3)

泛型在这种情况下并不真正有用,因为大多数类型将在运行时从输入字符串派生,而泛型是编译时的事情。无论如何都无法提前知道类型。它只在这样一个简单的例子中才真正有用。

所以我的建议是放弃泛型并实现自己的动态类型系统。例如:

enum Type {
    Integer,
    Boolean;
}

class ScriptObject {
    private final Object value;
    private final Type type;

    private ScriptObject(Object value, Type type) {
        this.value = value;
        this.type = type;
    }

    public static ScriptObject of(boolean b) {
        return new ScriptObject(b, Type.Boolean);
    }

    public static ScriptObject of(int i) {
        return new ScriptObject(i, Type.Integer);
    }

    public int asInt() {
        return (int) value;
    }

    public boolean asBoolean() {
        return (boolean) value;
    }

    public static boolean areType(Type type, ScriptObject...objects) {
        for(ScriptObject o : objects) {
            if(o.type != type)
                return false;
        }

        return true;
    }

    @Override
    public String toString() {
        return value.toString();
    }

}

abstract class Expression {
    public abstract ScriptObject getValue();
}

class BasicExpression extends Expression {
    private final ScriptObject value;

    public BasicExpression(ScriptObject value) {
        this.value = value;
    }

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

}

abstract class BinaryExpression extends Expression {
    private final Expression left;
    private final Expression right;

    public BinaryExpression (Expression l, Expression r) {
        this.left = l;
        this.right = r;
    }

    @Override
    public ScriptObject getValue() {        
        return operation(left.getValue(), right.getValue());
    }

    protected abstract ScriptObject operation(ScriptObject left, ScriptObject right);

}

转换示例将如下所示:

public static void main(String[] args) {
    Expression left1 = new BasicExpression(ScriptObject.of(42));
    Expression right1 = new BasicExpression(ScriptObject.of(3));     
    Expression sum = createSumExpression(left1, right1);
    System.out.printf("%s + %s = %s%n",left1.getValue(), right1.getValue(), sum.getValue());

    Expression left2 = new BasicExpression(ScriptObject.of(true));
    Expression right2 = new BasicExpression(ScriptObject.of(false));
    Expression and = createAndExpression(left2, right2);
    System.out.printf("%s && %s = %s%n",left2.getValue(), right2.getValue(), and.getValue());

    createAndExpression(left1, right2).getValue(); // fails with: Can not apply '&' to '42' and 'false' 
}

private static Expression createSumExpression(Expression left, Expression right) {
    return new BinaryExpression(left, right) {
        @Override
        protected ScriptObject operation(ScriptObject left, ScriptObject right) {
            if(!ScriptObject.areType(Type.Integer, left, right)) {
                throw new RuntimeException("Can not apply '+' to '" + left + "' and '" + right + "'");
            }
            return ScriptObject.of(left.asInt() + right.asInt());
        }
    };
}

private static Expression createAndExpression(Expression left, Expression right) {
    return new BinaryExpression(left, right) {
        @Override
        protected ScriptObject operation(ScriptObject left, ScriptObject right) {
            if(!ScriptObject.areType(Type.Boolean, left, right)) {
                throw new RuntimeException("Can not apply '&' to '" + left + "' and '" + right + "'");
            }
            return ScriptObject.of(left.asBoolean() && right.asBoolean());
        }
    };
}

我试图让这个例子保持简单,但是有些注意事项是你可能想要为类型检查创建一堆辅助函数,并且还使用自定义异常类型,可能是一个已经检查过的例子并在某处输出它的消息,而不仅仅是崩溃。

你必须自己进行类型检查,这是更多的工作,但优点是你可以完全决定何时(甚至是)你检查类型,以及提供什么样的错误信息。

还有创建类型转换机制的空间,因此您可以尝试将其转换为所需的类型,而不仅仅是自动(除了自动)之外,还可以尝试将其转换为所需的类型。 un)拳击)Java的类型系统。

答案 1 :(得分:0)

如果您更倾向于编译时类型安全而不检查由于可能的运行时故障导致的异常,您还可以定义多个工厂。

如果为某种类型调用无效方法,仍然会抛出异常,但是您无法创建错误类型的表达式。

例如,您可以使用ExpressionFactory仅处理Integer个表达式。

private static class IntegerExpressionFactory implements IExpressionFactory<Expression<Integer>> {

    @Override
    public Expression<Integer> createSumExpression(Expression<Integer> left, Expression<Integer> right) {
        return new BinaryExpression<>(left, right) {
            @Override
            protected Integer operation(Expression<Integer> left, Expression<Integer> right) {
                return left.getValue() + right.getValue();
            }
        };
    }

    @Override
    public Expression<Integer> createAndExpression(Expression<Integer> left, Expression<Integer> right) {
        throw new UnsupportedOperationException("Can't perform AND operation on integer expressions.");
    }
}

它会对您现有的代码产生以下影响:

public static void main(String[] args) {
    IntegerExpressionFactory integerExpressionFactory = new IntegerExpressionFactory();
    Expression<Integer> left1 = new BasicExpression<>(42);
    Expression<Integer> right1 = new BasicExpression<>(3);
    Expression<Integer> sum = integerExpressionFactory.createSumExpression(left1, right1);
    System.out.printf("%d + %d = %d%n", left1.getValue(), right1.getValue(), sum.getValue()); // 42 + 3 = 45

    integerExpressionFactory.createAndExpression(left1, right1); // UnsupportedOperationException

    Expression<Boolean> left2 = new BasicExpression<>(true);
    Expression<Boolean> right2 = new BasicExpression<>(false);
    integerExpressionFactory.createAndExpression(left2, right2); // Does not compile
}

它不如自定义类型系统灵活,在自定义类型系统中,您可以组合多种类型的操作,但这是一种更直接的实现。

答案 2 :(得分:-2)

因为你无法在运行时访问这些信息,所以没有办法明确地知道泛型的类型是什么,而没有很多看起来很难看的try / catch块。因此,在方法签名之前添加@SuppressWarnings("unchecked")行,并确保清理输入。

@SuppressWarnings("unchecked")
private static Expression createSumExpression(Expression left, Expression right) { // Raw types because of given interface
    return new BinaryExpression<Integer,Expression<Integer>,Expression<Integer>>(left, right) {
        @Override
        protected Integer operation(Expression<Integer> left, Expression<Integer> right) {
            return left.getValue() + right.getValue();
        }
    };
}

@SuppressWarnings("unchecked")
private static Expression createAndExpression(Expression left, Expression right) { // Raw types because of given interface
    return new BinaryExpression<Boolean,Expression<Boolean>,Expression<Boolean>>(left, right) {
        @Override
        protected Boolean operation(Expression<Boolean> left, Expression<Boolean> right) {
            return left.getValue() & right.getValue();
        }
    };
}

@SuppressWarnings("unchecked")将告诉java编译器不要担心未经检查的转换并继续编译。

清理您的输入,只有Expression<Integer>Expression<Double>仅与createSumExpression()一起使用。表达式的boolean侧也是如此