Обобщения и безопасность типов: проверка динамического связывания аргументов
Я застрял, пытаясь выяснить, как я могу использовать универсальные средства, чтобы проверить, является ли данный объект из суперкласса желаемым объектом из одного из подклассов подкласса этого суперкласса. Позвольте мне привести пример:
Допустим, у нас есть эта иерархия:
public interface Expression{
public Object evaluate();
}
public abstract class BooleanExpression implements Expression{
public abstract Boolean evaluate();
}
public abstract class PositionExpression implements Expression{
public abstract Integer[] evaluate();
}
public class Conjunction extends BooleanExpression{
public Boolean evaluate()
}
public class BooleanTrue extends BooleanExpression{
@Override
public Boolean evaluate(){
return true;
}
}
Теперь я хочу сказать, что программа должна иметь возможность создавать объект из Conjunction только тогда и только тогда, когда заданные аргументы в конструкторе являются либо BooleanExpression, либо подклассом этого класса.
Я пытался использовать дженерики, как это:
public class Conjunction<T extends BooleanExpression> extends BooleanExpression{
public Conjuction(T left, T right){
this.left = left;
this.right = right;
}
private T left, right;
@Override
public Boolean evaluate(){
return (left.evaluate() && right.evaluate())
}
Когда я хочу создать экземпляр, у меня есть следующий код:
public Expression createConjunction(Expression left, Expression right){
return new Conjunction<BooleanExpression>(left, right)
}
Но, к сожалению, это не компилируется! Я хочу использовать дженерики, чтобы проверить, left
а также right
являются примером BooleanExpression
потому что Conjunction
может быть только между двумя логическими значениями (а не PositionExpression
). Тем не мение, left
а также right
может быть другим BooleanExpression
, left
может быть Conjunction
в то время как right
может быть BooleanTrue
(как пример).
Итак, резюме: я хочу иметь возможность создать экземпляр Conjunction
когда оба приведены аргументы left
а также right
являются подклассом BooleanExpression. Создание экземпляра Conjuction
с одним из аргументов, являющихся подклассом PositionExpression
не должен быть принят компилятором.
Я хочу решить это без изменения createConjunction
метод и с использованием общих классов / интерфейсов. Есть идеи?
2 ответа
public Expression createConjunction(Expression left, Expression right){
return new Conjunction<BooleanExpression>(left, right)
}
в вышеуказанном методе вы запрашиваете 2 expression
объекты, но в conjuction
конструктор запрашивает 2 объекта, который расширяет Booleanexpressions
это та часть, в которой вы получаете ошибку компиляции.
Вы можете изменить свой метод createConjuction на
public Expression createConjunction(BooleanExpression left, BooleanExpression right){
return new Conjunction<BooleanExpression>(left, right)
}
или вы можете изменить generic
выражение в conjuction
класс для
Conjunction<T extends Expression> extends BooleanExpression{}
Если вы хотите сохранить сигнатуру метода createConjunction и не хотите изменять класс Conjunction, то, возможно, вы можете проверить и привести тип аргументов в методе createConjunction.
public Expression createConjunction(Expression left, Expression right){
if( !(left instanceof BooleanExpression) || !(right instanceof BooleanExpression)){
throw new IllegalArgumentException("wrong expression arguments, BooleanExpression expected");
}
BooleanExpression _left = (BooleanExpression)left;
BooleanExpression _right = (BooleanExpression)right;
return new Conjunction<BooleanExpression>(_left, _right);
}