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

Java 在运行时强制转换之前检查泛型类型,java,generics,casting,Java,Generics,Casting,我们的任务是创建用于计算表达式的对象结构(例如“1+2”或“true&false”)。提供了解析器以及用于创建表达式对象的工厂接口 我们对不同的表达式使用泛型:Expression返回一个整数,Expression返回一个布尔值,等等 我们面临的问题是,提供的接口使用原始类型表达式,例如: public Expression createSumExpression(Expression left, Expression right); 仅当操作数的类型为expression或expressio

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

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

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

public Expression createSumExpression(Expression left, Expression right);
仅当操作数的类型为
expression
expression
时,才定义求和表达式。我们怎么检查这个? 由于类型擦除,类型信息在运行时不可用,如果类型信息不正确,只会导致
ClassCastException

我可以修改所有内容,除了
createSumExpression
createAndExpression
函数


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

主类
公共类主{
公共静态void main(字符串[]args){
表达式left1=新的基本表达式(42);
表达式right1=新的基本表达式(3);
表达式和=createSumExpression(left1,right1);
System.out.printf(“%d+%d=%d%n”,left1.getValue(),right1.getValue(),sum.getValue());
表达式left2=新的基本表达式(true);
表达式right2=新的基本表达式(false);
Expression and=createAndExpression(left2,right2);
System.out.printf(“%b&%b=%b%n”、left2.getValue()、right2.getValue()和.getValue());
}
私有静态表达式createSumExpression(表达式左,表达式右){//Raw类型,因为给定接口
返回新的二进制表达式(左、右){
@凌驾
受保护的整数操作(表达式左、表达式右){
返回left.getValue()+right.getValue();
}
};
}
私有静态表达式createAndExpression(表达式左,表达式右){//由于给定接口,原始类型
返回新的二进制表达式(左、右){
@凌驾
受保护的布尔运算(表达式左、表达式右){
返回left.getValue()&right.getValue();
}
};
}
}
Expression.java
抽象公共类表达式{
公共抽象V getValue();
}
BasicExpression.java
public类基本表达式扩展了表达式{
公共基本表达式(V值){
这个值=值;
}
@凌驾
public V getValue(){
返回值;
}
私人价值;
}
BinaryExpression.java
抽象公共类二进制表达式扩展表达式{
公共二进制表达式(L,R){
这个左=l;
这个。右=r;
}
@凌驾
public V getValue(){
返回操作(左、右);
}
抽象保护V操作(左左,右);
列兵L左;
私权;
}

提供的接口是:

/**
 * @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;
    // ...
}
/**
*@param
*用于表示表达式的类。
*/
公共接口IExpressionFactory{
公共E createSumExpression(E left,E right)抛出ModelException;
公共E createAndExpression(E left,E right)抛出ModelException;
// ...
}

因为您无法在运行时访问该信息,所以如果没有大量的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(“未选中”)
私有静态表达式createSumExpression(表达式左,表达式右){//Raw类型,因为给定接口
返回新的二进制表达式(左、右){
@凌驾
受保护的整数操作(表达式左、表达式右){
返回left.getValue()+right.getValue();
}
};
}
@抑制警告(“未选中”)
私有静态表达式createAndExpression(表达式左,表达式右){//由于给定接口,原始类型
返回新的二进制表达式(左、右){
@凌驾
受保护的布尔运算(表达式左、表达式右){
返回left.getValue()&right.getValue();
}
};
}
@SuppressWarnings(“unchecked”)
将告诉java编译器不要担心未经检查的强制转换,继续编译


清理输入,以便只有
Expression
Expression
createSumExpression()
一起使用。表达式的
boolean
端也是如此,泛型在这种情况下并不真正有用,因为大多数类型都是在运行时从输入字符串派生的,泛型是编译时的东西。没有任何方法可以提前知道这些类型。它只在这样一个简单的例子中才真正有用

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

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());
        }
    };
}
我试图使示例保持简单,但有一些注意事项是,您可能希望为类型检查创建一组帮助函数,并使用自定义异常类型,可能是一个已检查的异常类型,然后捕获并将其消息输出到某处,而不只是崩溃

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

还存在为类型创建转换机制的空间,因此不只是检查类型是否为w
/**
 * @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;
    // ...
}
@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();
        }
    };
}
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());
        }
    };
}
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
}