How do I write a Java function that returns a typed instance of 'this' and works when extended?

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?


Asked by: Roman917 | Posted: 23-01-2022






Answer 1

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.

Answered by: Melissa424 | Posted: 24-02-2022



Answer 2

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.

Answered by: Gianna553 | Posted: 24-02-2022



Answer 3

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.

Answered by: Sawyer421 | Posted: 24-02-2022



Answer 4

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.

Answered by: Maya929 | Posted: 24-02-2022



Answer 5

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.

Answered by: Julia732 | Posted: 24-02-2022



Answer 6

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

Answered by: Emma169 | Posted: 24-02-2022



Answer 7

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.

Answered by: Emily472 | Posted: 24-02-2022



Similar questions

java - Generics in for each loop problem if instance does not have generic type assigned

This question already has answers here:


generics - How do I return an instance of an object of the same type as the class passed in using Java 6?

I want to return an instance of an object of same type of the Class object passed in. The type passed in can be ANYTHING. Is there a way to do this with Generics? To clarify -- I don't want the caller of the method to not have to cast to the Class of the object they passed in For example, public Object&lt;Class&gt; getObject(Class class) { // Construct an instance of an object of type Clas...


Java Generics - get instance of class

I have an abstract class called Data, with a getInstance() method which should return instances of concrete subclasses of Data. I want to pass a String to the getInstance method, and this string will define exactly what class will be returned. So far I have: public abstract class Data { private Map&lt;String, Data&gt; instances; public &lt;T extends Data&gt; T getInstance(Class&lt;T&g...


Java generics for class and new instance

In Java, if I have: public class Foo&lt;T&gt; { public Foo() { } } I can do Foo&lt;Integer&gt; foo = new Foo&lt;Integer&gt;(); but I cannot find an equivalent syntax for using the class field, meaning that something like Foo&lt;Integer&gt; foo = Foo&lt;Integer&gt;.class.newInstance(); doesn't work. Is there a way to use generics via the newInstan...


java - Generics with multi threads and instance sharing

I have a generic class that can be sketched like this public class Shared&lt;T extends EntityBase&gt; { private HashMap&lt;String, Class&lt;? extends Executor&lt;T&gt;&gt;&gt; classes; private HashMap&lt;String, ? super Executor&lt;T&gt;&gt; instances; private List&lt;Entity&gt; entities; private Compiler compiler; // Constructor and getters public void put(Entity entity, Source so...


Java Generics, Create an instance of Class<T>

I am trying to write a generic method for deserializing json into my model. My problem is that I don't know how to get Class from the generic type T. My code looks something like this (and doesn't compile this way) public class JsonHelper { public &lt;T&gt; T Deserialize(String json) { Gson gson = new Gson(); return gson.fromJson(json, Class&lt;T&gt;); } } I...


generics - Java - Get instance of each subclass

I have an abstract class Command. The constructor looks like this: public Command(String cmd, String helpMsg, String... args) { this.command = cmd; this.helpMsg = helpMsg; this.args = args; } Whenever a certain condition is met, I want to be able to print out each command, it's help message and arguments. If I could loop through the instances of the subclasses of Command, ...


java - Generics in legacy code

We've got a fairly large amount of code that just made the jump to Java 5. We've been using generics in those components targeted at being released in the Java 5 version, but the remaining code is, of course, full of raw types. I've set the compiler to generate an error for raw types and started manually clearing them, but at the present rate it'll take a very long time to go through with it (there are about 2...


Java Generics Syntax for arrays

What data structure does the following declaration specify? List&lt;ArrayList&gt;[] myArray; I think it should declare an array where each element is a List (e.g., a LinkedList or an ArrayList) and require that each List contain ArrayList objects. My reasoning: List&lt;String&gt; someList; ...


generics - Java Class Type

I have a block of code that works and I wanted to ask what exactly is happening here? Class&lt;?&gt; normalFormClass = null; ---added--- The wildcard "&lt;?&gt;" is the part that is confusing for me. Why would this be used rather than not using it (generics)?


How to use generics in a world of mixed Java versions?

I like generics a lot and use them whereever I can. Every now and then I need to use one of my classes in another project which has to run on an old JVM (before 5.0), needs to run on JavaME (where generics are not allowed neither) or in Microsoft J# (which has VERY poor Support for generics). At the moment, I remove all generics manually, which means inserting many casts as well. Since generics are said to ...


C# vs Java generics

This question already has answers here:


In Java, when should I use "Object o" instead of generics?

For example, I could write either of these: class example &lt;T&gt; { ... public void insert (T data) { ... } } or class example { ... public void insert (Object o) { ... } } Is there a signficant difference between the 2 in terms of performance? With generics I could restrict the type of the parameter and ...


generics - Why can't the Java compiler figure this out?

Why is the compiler unable to infer the correct type for the result from Collections.emptySet() in the following example? import java.util.*; import java.io.*; public class Test { public interface Option&lt;A&gt; { public &lt;B&gt; B option(B b, F&lt;A,B&gt; f); } public interface F&lt;A,B&gt; { public B f(A a); } public Collection&lt;String&gt; getColl() { Opt...


java - How do you access Class object for generics?

How can I access Class object for Generics? Currently, I am doing it this way: List&lt;String&gt; list= new ArrayList&lt;String&gt;(); list.getClass(); Is this OK? Or, what should be the way?


How do I fix this Java generics wildcard error?

In this question, TofuBeer was having problems creating a genericized IterableEnumeration. The answer came from jcrossley3 pointing to this link http://www.javaspecialists.eu/archive/Issue107.html which pretty much solved the p...


Java: Generics won't work for my method, what else can I do?

In the code below, I would like the second method to be generic, too, but since I create the Calendar object inside the method, because of type erasure, I don't see how. One possibility would be to pass in the Calendar object, but that would defeat the main purpose for having this method at all (not h...






Still can't find your answer? Check out these amazing Java communities for help...



Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)



top