繁体   English   中英

在运行时转换之前检查泛型类型

[英]Check generic type before casting at runtime

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

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

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

public Expression createSumExpression(Expression left, Expression right);

sum表达式仅在操作数类型为Expression<Integer>Expression<Double> 我们怎么检查这个? 由于类型擦除,类型信息在运行时不可用,如果它不正确,它只会导致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;
    // ...
}

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

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

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());
        }
    };
}

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

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

还有空间为类型创建转换机制,因此您可以尝试将其转换为所需的类型,而不仅仅是自动(非)拳击,而不是仅仅检查类型是否是您想要的类型。 )Java的类型系统。

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

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

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

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
}

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

因为你无法在运行时访问这些信息,所以没有办法明确知道泛型的类型是什么,而没有很多看起来很难看的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方面也是如此

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM