user4758246
user4758246

Reputation: 585

Java avoiding instanceof by design

Consider the following class design:

public class SuperType { };
public class SubType1 extends SuperType { };
public class SubType2 extends SuperType { };
public class SubType3 extends SuperType { };

public class Tuple {
    SuperType t1;
    SuperType t2;

    public Tuple(SuperType t1, SuperType t2) {
        this.t1 = t1;
        this.t2 = t2;
    }

    public void DoSomething1() {
        if((t1 instanceof SubType1) && (t2 instanceof SubType3))
            switch(t1, t2);
        else if((t1 instanceof SubType2) && (t2 instanceof SubType1)) 
             t1.DoSomething();
        else if( ... ) {
             t1.DoSomething();
             t2.DoSomething();
        }
        else if( ... )
            // ...
    }

    public void DoSomething2() {
        // basically the same
    }
}

Since the action is dependent on two types I cant avoid the instanceof operator by moving the method to the subtypes. Is there a way I can improve my design so I can avoid using instanceof?

I know there are a lot of similar questions here, but I'd like to avoid the use of a visitor, because I have around twenty DoSomething()-Methods which would result in 9*20 implementations of visit().

Upvotes: 0

Views: 149

Answers (2)

Husam
Husam

Reputation: 2079

Here an simple solution option you can use it, using Generics for the type safe & Polymorphism to do the actions behavior in each of subclass

interface SuperType<T> { void doAction(); };
class SubType1 implements SuperType<SubType1> { public void doAction(){} };
class SubType2 implements SuperType<SubType2> { public void doAction(){} };
class SubType3 implements SuperType<SubType3> { public void doAction(){} };

class Tuple {

   //if((t1 instanceof SubType1) && (t2 instanceof SubType3))
   //passing params rather than instanceof checking
   public void doSomething1CaseOne(SuperType<? extends SubType1> t1, SuperType<? extends SubType3> t3) {
       if(t1 == null || t3 == null) {
           throw new NullPointerException("Null Params!");
       }
       /**
        * the method action here
        * example: switch(t1, t2);
        */
   }

   //if((t1 instanceof SubType2) && (t2 instanceof SubType1))
   public void doSomething1CaseTwo(SuperType<? extends SubType2> t2, SuperType<? extends SubType1> t1) {
       if(t2 == null || t1 == null) {
           throw new NullPointerException("Null Params!");
       }
       /**
        * the method action here
        * example: t1.doAction();
        */
   }
   ... others methods
}

Now here an example for passing valid/invalid params to the method:

Tuple tuple = new Tuple();
SuperType<SubType1> t1 = new SubType1();
SuperType<SubType2> t2 = new SubType2();
SuperType<SubType3> t3 = new SubType3();

tuple.doSomething1CaseOne(t1, t3); //valid typesafe compilation time
tuple.doSomething1CaseOne(t1, t2); //invalid typesafe compilation time

tuple.doSomething1CaseTwo(t2, t1); //valid typesafe compilation time
tuple.doSomething1CaseTwo(t1, t2); //invalid typesafe compilation time

Upvotes: 0

Matt Timmermans
Matt Timmermans

Reputation: 59358

The proper way to do this in an OO language is using a pattern called "double-dispatch" (Googlable, but wikipedia's page on it is not great).

An "append" method makes a good example:

class Super
{
    abstract void appendTo(Super target);
    abstract void append(Sub1 source);
    abstract void append(Sub2 source);
}

class Sub1
{
    void appendTo(Super target)
    {
        target->append(this); //calls the Sub1 overload
    }
    void append(Sub1 source)
    {
        ... this is Sub1, source is Sub1 ...
    }
    void append(Sub2 source)
    {
        ... this is Sub1, source is Sub2 ...
    }
}
class Sub2
{
    void appendTo(Super target)
    {
        target->append(this); //calls the Sub2 overload
    }
    void append(Sub1 source)
    {
        ... this is Sub2, source is Sub1 ...
    }
    void append(Sub2 source)
    {
        ... this is Sub2, source is Sub2 ...
    }
}

Upvotes: 1

Related Questions