How to achieve method chaining in Java?
Asked Answered
C

4

59

I want to achieve method chaining in Java.

How can I achieve it?

Also let me know when to use it.

public class Dialog {

     public Dialog() {

     }

     public void setTitle(String title) {

         //Logic to set title in dialog
     }

     public void setMessage(String message) {

         //Logic to set message
     }     

     public void setPositiveButton() {

         //Logic to send button
     }
}   

I want to create method chaining that I can use as follows:

new Dialog().setTitle("Title1").setMessage("sample message").setPositiveButton();

or like

new Dialog().setTitle("Title1").setMessage("sample message");

or like

new Dialog().setTitle("Title1").setPositiveButton();
Colossians answered 17/1, 2014 at 7:32 Comment(4)
Like the Builder pattern?Naphthalene
This is typically called fluency or fluent programming.Karlotte
You would create a Builder for the Dialog instead of using the setters for a fluent api. If you do so many bean like access will no longer work as the frameworks restrict setters to return void. Have a look at project lombok if you don't like to write all that boiler plate.Pressey
Lombok experimental annotation, with fluent=true: projectlombok.org/features/experimental/AccessorsAdlib
C
116

Have your methods return this like:

public Dialog setMessage(String message)
{
    //logic to set message
    return this;
}

This way, after each call to one of the methods, you'll get the same object returned so that you can call another method on.

This technique is useful when you want to call a series of methods on an object: it reduces the amount of code required to achieve that and allows you to have a single returned value after the chain of methods.

An example of reducing the amount of code required to show a dialog would be:

// Your Dialog has a method show() 
// You could show a dialog like this:
new Dialog().setMessage("some message").setTitle("some title").show();

An example of using the single returned value would be:

// In another class, you have a method showDialog(Dialog)
// Thus you can do:
showDialog(new Dialog().setMessage("some message").setTitle("some title"));

An example of using the Builder pattern that Dennis mentioned in the comment on your question:

new DialogBuilder().setMessage("some message").setTitle("some title").build().show();

The builder pattern allows you to set all parameters for a new instance of a class before the object is being built (consider classes that have final fields or objects for which setting a value after it's been built is more costly than setting it when it's constructed).

In the example above: setMessage(String), setTitle(String) belong to the DialogBuilder class and return the same instance of DialogBuilder that they're called upon; the build() method belongs to the DialogBuilder class, but returns a Dialog object the show() method belongs to the Dialog class.

Extra

This might not be related to your question, but it might help you and others that come across this question.

This works well for most use cases: all use cases that don't involve inheritance and some particular cases involving inheritance when the derived class doesn't add new methods that you want to chain together and you're not interested in using (without casting) the result of the chain of methods as an object of the derived.

If you want to have method chaining for objects of derived classes that don't have a method in their base class or you want the chain of methods to return the object as a reference of the derived class, you can have a look at the answers for this question.

Chroma answered 17/1, 2014 at 7:35 Comment(2)
I've added the example uses last as I missed part of your question. Do note that these are not the only uses for method chaining and/or Builder pattern: as with most coding patterns, algorithms and techniques, with a bit of creativity a new use for an old pattern (algorithm, technique) can be found :)Chroma
It is not good practice to change the settings behavior, because some frameworks are not expecting any return from setter method. Like for example, you will get an error with Cassandra Java driver if the setters in your model will return some values.Raceway
R
9

Just add a static builder method, and create another set of the setter methods. For example

class Model {
   private Object FieldA;

   private Object FieldB;

   public static Model create() {
       return new Model();
   }

   public Model withFieldA(Object value) {
       setFieldA(value);
       return this;
   }

   public Model withFieldB(Object value) {
       setFieldB(value);
       return this;
   }
}

...

And use it like

 Model m = Model.create().withFieldA("AAAA").withFieldB(1234);
Raceway answered 21/6, 2016 at 22:48 Comment(0)
P
1

example of reducing the amount of code required to show a dialog would be:

package com.rsa.arraytesting;

public class ExampleJavaArray {

String age;
String name;

public ExampleJavaArray getAge() {
    this.age = "25";
    return this;
}

public ExampleJavaArray setName(String name) {
    this.name = name;
    return this;
}
public void displayValue() {
    System.out.println("Name:" + name + "\n\n" + "Age:" + age);
}
}

another class

package com.rsa.arraytesting;

public class MethodChaining {

public static void main(String[] args) {

    ExampleJavaArray mExampleJavaArray = new ExampleJavaArray();

    mExampleJavaArray.setName("chandru").getAge().displayValue();

}

}
Physic answered 14/4, 2015 at 11:14 Comment(1)
package com.rsa.arraytesting; public class MethodChaining { public static void main(String[] args) { ExampleJavaArray mExampleJavaArray = new ExampleJavaArray(); mExampleJavaArray.setName("chandru").getAge().displayValue(); } }Physic
D
0

In case if you are using lombok, you can use parameter in your lombok.config:

lombok.accessors.chain = true

Or for particular data classes you can declare @Accessors(chain = true) annotation:

import lombok.experimental.Accessors;

@Accessors(chain = true)
@Data
public class DataType {

    private int value;

    // will generate setter:
    public DataType setValue(int value) {
        this.value = value;
        return this;
    }

}
Dishonest answered 29/8, 2020 at 19:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.