0

I am stuck trying to figure out how I can use generics to check if the given object from a superclass, is the desired object from one of the subclasses of a subclass of this superclass. Let me give an example:

Let's say we have a this hierarchy:

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

Now my point is that the program should only be able to construct an object from Conjunction if and only if the given arguments in the constructor are either a BooleanExpression or a subclass of this class.

I have tried using generics like this:

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

When I want to create an instance, I have the following code:

public Expression createConjunction(Expression left, Expression right){
     return new Conjunction<BooleanExpression>(left, right)
}

But unfortunately this doesn't compile! I want to use generics to check whether left and right are instance of BooleanExpression, because a Conjunction can only be between two boolean values (and not a PositionExpression). However, left and right can be different BooleanExpression, left could be a Conjunction while right could be a BooleanTrue (as example).

So a recap: I want to be able to create an instance of Conjunction when both given arguments left and right are a subclass of BooleanExpression. Creating an instance of Conjuction with one of the argument being a subclass of PositionExpression shouldn't be accepted by the compiler.

I want to solve this without changing the createConjunction method and with using generic classes/interfaces. Any ideas?

  • You will definitely have to change the `createConjunction` function since in its current form it simply is invalid. All you should have to change is to make the types of both `left` and `right` `BooleanExpression` instead of `Expression`. – luk2302 May 15 '16 at 16:24
  • The compiler is doing exactly what it's supposed to. It doesn't compile because `left` and `right` are `Expression`s, but your `Conjuction` constructor explicitly requires those to be subclasses of `BooleanExpression`. – blacktide May 15 '16 at 16:25
  • @Casey I guess there is no way for the compiler to check if the underlying object of `left` is of the subclass `BooleanExpression`, is there? (e.g. when I call this method `createConjunction`, the `left` argument could contain a `BooleanTrue` which is a subclass of `BooleanExpression` (which implements the `Expression` interface)) – Kevin Algoet May 15 '16 at 16:38
  • @luk2302 Yeah I hoped that I wouldn't have to do that, because in my current project I'm not allowed to do this. Thank you for your answer though! – Kevin Algoet May 15 '16 at 16:40
  • The problem you are facing here is that the *compiler* does not know anything about the actual contents of `left` and `right`. The compiling happens before runtime. And what should happen to the method at runtime if the objects do in fact not contain a suitable Expression? return null? throw an error? That are far too many open questions. – luk2302 May 15 '16 at 16:41
  • @luk2302 Yeah I'm sorry about the vagueness, first question! I'll edit the thread to clarify! But according to my documentation it should indeed return an exception, stating that it is simply not possible to create an `Conjunction` with the given `Expression`s. – Kevin Algoet May 15 '16 at 16:54
  • That is certainly possible. BUT that is something that **you** have to implement. Nothing that the runtime environment can on its own for you. – luk2302 May 15 '16 at 16:55
  • your use of generics in Conjunction are serving no purpose whatsoever? just use BooleanExpression as your type. – jtahlborn May 16 '16 at 01:49

2 Answers2

1
public Expression createConjunction(Expression left, Expression right){
     return new Conjunction<BooleanExpression>(left, right)
}

in the above method , you are requesting 2 expression objects but in the conjuction constructor it is requesting 2 objects which extends Booleanexpressions this is the part you are getting the compile error.

you can change your createConjuction method to

 public Expression createConjunction(BooleanExpression left, BooleanExpression right){
         return new Conjunction<BooleanExpression>(left, right)
    }

or you can change the generic expression in conjuction class to

 Conjunction<T extends Expression> extends BooleanExpression{}
Priyamal
  • 2,919
  • 2
  • 25
  • 52
0

If you do want to keep the signature of createConjunction method and don't want to change Conjunction class either, then you can probably check and cast the type of the arguments in the createConjunction method.

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);
}
Simon L
  • 173
  • 1
  • 11