tags:

views:

1391

answers:

7

Hi,

This is a bit of a lazyweb question but you get the rep so :-)

I have a Java class that returns instances of itself to allow chaining (e.g. ClassObject.doStuff().doStuff())

For instance:

public class Chainer
{
    public Chainer doStuff()
    {
       /* Do stuff ... */
       return this;
    }
}

I would like to extend this class. Is there a way, perhaps using generics, to extend this class without having to overwrite each method signature?

E.g. not:

public class ChainerExtender extends Chainer
{
    public ChainerExtender doStuff()
    {
       super.doStuff();
       return this;
    }
}

I have tried:

public class Chainer
{
    public <A extends Chainer> A doStuff()
    {
       /* Do stuff ... */
       return (A)this;
    }
}

public class ChainerExtender extends Chainer
{
    public <A extends Chainer> A doStuff()
    {
       /* Do stuff ... */
       return super.doStuff();
    }
}

But this didn't work giving the error:

type parameters of <A>A cannot be determined; 
no unique maximal instance exists for type variable A with upper bounds A,Chainer

Am I forced to have class declarations like:

public class Chainer<T extends Chainer<T>> {}
public class ChainerExtender extends Chainer<ChainerExtender>

As per this question?

+1  A: 

Could you just post a complete example leading to the error message you see ?

I just compiled and execute the following without a hitch:

public class Test {

    public static void main(String[] args) {
     // TODO Auto-generated method stub
     ChainerExtender c = new ChainerExtender();
     c.doStuff().doStuff();
    }

    public static class Chainer
    {
        public <A extends Chainer> A doStuff()
        {
           /* Do stuff ... */
         System.out.println("Chainer");
           return (A)this;
        }
    }

    public static  class ChainerExtender extends Chainer
    {
        /** 
         * @see test.Test.Chainer#doStuff()
         */
        @Override
     public <A extends Chainer> A doStuff()
        {
         System.out.println("ChainerExtender");
           return super.doStuff();
        }
    }

}

Gives:

ChainerExtender
Chainer
ChainerExtender
Chainer

I have have misunderstood this issue, please downvote and comment.

VonC
Thanks, though I guess I should use super.<ChainerExtender>doStuff() ?
Graphain
I'll check it out when I get a chance - thanks :-) And upvote coming in some amount of hours :P
Graphain
+5  A: 

Why not have them all return an interface? You'd have to define all of the possible chained methods in the interface and provide "null" implementations in the base class, but you'd be able to chain to your heart's content.

public interface IChainer
{
  IChainer doStuff();
  IChainer doSomethingElse();
}

public class Chainer implements IChainer
{
  public IChainer doStuff()
  {
     // really do something
     return this;
  }

  public IChainer doSomethingElse()
  {
      return this; // do nothing
  }
}

public class ChainerExtender extends Chainer
{
   // simply inherits implementation for doStuff()

   public override IChainer doSomethingElse()
   {
       // really do something
       return this;
   }
}

Note: I may have some syntax issues above. I've been programming mostly in C# for the last few years. Hopefully, you get the idea.

tvanfosson
Thanks. I was toying with an interface for other reasons but you are right that will solve the issue and no problem I use C# most of the time too :-).
Graphain
I don't like this. Suppose that you have some public interface, and several concrete implementations that you wish to configure with fluent programming. Pulling up implementation-specific configuration methods to the common interface will quickly lead to an ugly mess.
erickson
+1  A: 

I think that the simple answer is "no you can't". I can see how this might be useful but I feel that this use of inheritance screams "unexpected consequences" at me. It's only a matter of time before someone decides to add a bit of extra functionality in a subclass, as opposed to just doing a super.doStuff() and the control flow is going to be extremely difficult to follow.

One possible solution is for your chainers (if they are using the builder pattern) to return new instances of different builder classes instead of this. See what Stephen Colebourne has done with the date-creation builders in JSR 310 which is quite clever from an API perspective, although clunky to write.

oxbow_lakes
+1  A: 

It's been awhile since I've done any Java generics.

What about :


<T>
public class  Chainer 
{
    public T doStuf()
   {
   }

}

public class ChainerDerived : extends Chainer<ChainerDerived>
{
   public ChainerDerived doStuf()
   {
   }
}

I've mostly been doing C++ lately and this kind of stuff is just par for the course. Do Java generics support this type of structure?

I hope I understood your question.

+2  A: 

I got it to work this way. It uses a class literal to signal what type to cast to at runtime (see section 8 of Gilad Bracha's Generics Tutorial).

public class Chainer {
  public <T extends Chainer> T doStuff (Class<T> foo) {
    /* do stuff */
    return foo.cast (this);
  }
}

public class ChainerExtender extends Chainer {
  public <T extends ChainerExtender> doOtherStuff (Class<T> foo) {
    /* do other stuff */
    return foo.cast (this);
  }
}

So calls look like this:

Chainer c1 = new Chainer();
c1 = c1.doStuff (c1.getClass());
// ...
ChainerExtender ce1 = new ChainerExtender();
ce1 = ce1.doStuff (ce1.getClass()).doOtherStuff (ce1.getClass());

One downside to this technique is that, if you have a Chainer reference, you have to explicitly check if it's actually a ChainerExtender object before calling doOtherStuff on it. This makes sense, though, since trying to call the method on a Chainer object at runtime would yield an error.

shockwave
+5  A: 

Have you tried the straight-forward

public class Chainer
{
    public Chainer doStuff()
    {
       /* Do stuff ... */
       return this;
    }
}

public class ChainerExtender extends Chainer
{
    @Override
    public ChainerExtender doStuff()
    {
       /* Do stuff ... */
       super.doStuff();
       return this;
    }
}

With Java 5, you can declare overriding methods to have co-variant return types, which means just what this says: a subclass can have an overriding method signature with a more specific return type.

Dov Wasserman
Oh interesting - I'll have to check that out when I get time to review the project this relates to!
Graphain
+1  A: 

Hi all:

Notice that "super.<A>doStuff()" would compile and work. It hints generics about the type you are expecting.