0

I have a case where I have something like:

public abstract class Base{    
   private Base sibling;

   public Base( Base sibling ){
         this.sibling = sibling;
   }

   public Base getSibling(){
       return this.sibling;
   }
}

public class ChildA extends Base{
      private ChildB brother;

      public ChildA(){
          this.brother = new ChildB( this );
      }

      public ChildB getBrother(){ return this.brother }
      public void methodA(){ ... }
      public void methodB(){ ... }

}

public class ChildB extends Base{
   public childB someMethodX(){ ... }
   public childB someMethodY(){ ... }
}

Now I need it so that when doing something like:

   var child = new ChildA()
       .getBrother()
           .someMethodX()
           .someMethodY()
           .getSibling()
       .methodA()
       .methodB()

.getSibling() returns me back into the scope of ChildA. Currently however it seems to be returning me an instance of Base and giving me an error that methodA() does not exist in class Base.

I've looked at some cases of GenericTypes as possibly being what I want in this case, but I've struggled to implement it as of yet. Any examples/pointers/links would be greatly appreciated.

Thanks.

Roman Nikitchenko
  • 12,800
  • 7
  • 74
  • 110
SS44
  • 837
  • 2
  • 10
  • 26

2 Answers2

1

Unless by casting, you can't do what you want:

  var child = new ChildA()
       .getBrother() // ChildB 
           .someMethodX() // void
           .someMethodY() // compile error
           .getSibling()  // compile error (someMethodY = void), Base
       .methodA() // compile error, methodA does not exists on Base
       .methodB() // idem.

I propose you two methods to doing that:

  • a generic type on Base, so that Base can return this as the subtype rather than Base. If ChildA extends Base, with two methods ChildA::setFoobar, Base::setSaxon, then doing: builder.setSaxon("A").setFoobar("B") would be possible due to the generic.

    public class Base<B extends Base<B>> {
      protected abstract B self();
      public <C extends Base<C>> B setSibling(Base<C> base) {
        this.sibling = base;
        return self(); // or (B)this
      }
    }    
    public class ChildA extends Base<ChildA> {
      protected ChildA self() {return this;}
    }
    public class ChildB extends Base<ChildB> {}
    

    And this would work:

    ChildA a = new ChildA().setSibling(new ChildB().setSibling(new ChildA()));
    

    The self method will avoid a cast to (B)this in each setXXX method.

  • using a builder wrapper for B: return an intermediate object ChildBBuilder, whose "final" method getSibling() associated a new instance of ChildB to the ChildA it belongs, and return that object to continue the chain:

    public class ChildA extends Base{
      private ChildB brother;
      public ChildBBuilder getBrother() {
        return new ChildBBuilder() {
          public ChildA getSibling() {
            ChildA.this.brother = create();
            return ChildA.this;
          }
        };
      }
    }
    
    public class ChildBBuilder {
      public ChildBBuilder(ChildA parent) {
        this.parent = parent;
      }
      ... setter ...
    
      public ChildB create() {
        return new ChildB();
      }
    
      public ChildA getSibling() {
        throw new UnsupportedOperationException();
      }
    }
    

    ChildBBuilder is an independant builder allowing some getSibling operation whose default implementation is to fail. We create an anonymous abstract class to handle this case.

I already used in some of my project the first case, with the "base builder".

For the second case, I never used it. If you are trying to create a DSL for some of your work, don't forget it might be too much for what it's worth even if it's nice and all.

If you are working with Java 8 you can extends the pattern to a pure abstract implementation: see my answer here.

Community
  • 1
  • 1
NoDataFound
  • 11,381
  • 33
  • 59
1

This doesn't work because methodA is defined in ChildA but getSibling returns Base reference. Although this Base reference happens to point to ChildA object, you should either perform explicit castings or generalize Base type.

In this particular case, you may simply override getSibling:

class ChildB extends Base {
  public ChildB(ChildA sibling) {
    super(sibling);
  }

  @Override
  public ChildA getSibling() {
    return (ChildA) super.getSibling();
  }
}

This would not scale well if there are a lot of classes like ChildB though. Also, ChildB is restricted to contain only ChildA as it's sibling.


Edit:

it may not always be childB

Alright, assuming you don't need Base class to be generic, you may replace it with:

class ChildB <T extends Base> extends Base {
    public ChildB(T sibling) {
      super(sibling);
    }


    @Override
    @SuppressWarnings("unchecked")
    public T getSibling() {
      return (T) super.getSibling();
    }
}

You may instantiate it as follows:

brother = new ChildB<ChildA>(this);
lifus
  • 8,074
  • 1
  • 19
  • 24
  • Problem with this solution for my case is that ChildB may be included in other subclasses of Base, so getSibling should return those. i.e, it may not always be childB – SS44 Sep 24 '14 at 14:54
  • Okay, what about second option? – lifus Sep 24 '14 at 15:12