How to achieve method chaining in Java?
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.
How to do method chaining in Java? o.m1().m2().m3().m4()
This pattern is called "Fluent Interfaces" (see Wikipedia)
Just return this;
from the methods instead of returning nothing.
So for example
public void makeText(String text) {
this.text = text;
}
would become
public Toast makeText(String text) {
this.text = text;
return this;
}
Method Chaining in Java
Eh. There's readability arguments to be made in both directions -- there's such a thing as trying to put too much into a single line.
But honestly, I suspect here it's for historical reasons: pervasive "chaining" behavior hadn't really become popular or well-known when e.g. Swing was being developed. You could argue that it should've been added in later on, but things like that tend to create binary incompatibilities and other issues that Sun/Oracle have historically been extremely cautious about.
More recent JDK libraries -- see e.g. ByteBuffer
for a major, well-known example -- have provided chaining behavior and the like, where it makes sense.
Method chaining - why is it a good practice, or not?
I agree that this is subjective. For the most part I avoid method chaining, but recently I also found a case where it was just the right thing - I had a method which accepted something like 10 parameters, and needed more, but for the most time you only had to specify a few. With overrides this became very cumbersome very fast. Instead I opted for the chaining approach:
MyObject.Start()
.SpecifySomeParameter(asdasd)
.SpecifySomeOtherParameter(asdasd)
.Execute();
The method chaining approach was optional, but it made writing code easier (especially with IntelliSense). Mind you that this is one isolated case though, and is not a general practice in my code.
The point is - in 99% cases you can probably do just as well or even better without method chaining. But there is the 1% where this is the best approach.
Function chaining in Java
Given that you already implemented your Filters
utility class you can easily define a list of filter functions
List<Function<String,String>> filterList = new ArrayList<>();
filterList.add(Filters::removeUrl);
filterList.add(Filters::removeRepeatedWhitespace);
...
and then evaluate:
String text = ...
for (Function<String,String> f : filterList)
text = f.apply(text);
A variation of this, even easier to handle:
Define
public static String filter(String text, Function<String,String>... filters)
{
for (Function<String,String> f : filters)
text = f.apply(text);
return text;
}
and then use
String text = ...
text = filter(text, Filters::removeUrl, Filters::removeRepeatedWhitespace);
How to chain multiple java method calls in a single statement?
This is a perfectly valid question, and yes: it is possible.
The type of api style is called Fluent interface. From wikipedia:
A fluent interface is normally implemented by using method cascading (concretely method chaining) to relay the instruction context of a subsequent call
To implement this for your use case, just declare your methods like this:
public MyClass params(String url, String body) {
// your code here
return this;
}
instead of that:
public void params(String url, String body) {
// your code here
}
Custom Iterator and method chaining in Java
As Iterator
accepts one parameter, here is how I modified your code.
public class ChainedIterator<T> {
private Function<T, ?> action;
private ChainedIterator<T> chain;
private final Iterator<?> iterator;
private <R> ChainedIterator(Iterator<?> iterator, Function<T, R> action, ChainedIterator<T> prev) {
this.action = action;
this.chain = prev;
this.iterator = iterator;
}
public static <T> ChainedIterator<T> fromIterator(Iterator<T> iterator) {
return new ChainedIterator<>(iterator, Function.identity(), null);
}
public T next() {
return (T) this.action.apply((T) (Objects.nonNull(this.chain) ? this.chain.next() : this.iterator.next()));
}
public boolean hasNext() {
return this.iterator.hasNext();
}
public <R> ChainedIterator<R> map(Function<T, R> action) {
return new ChainedIterator(this.iterator, action, this);
}
public void forEach(Consumer<T> action) {
while (hasNext()) {
action.accept(this.next());
}
}
}
Usage Example
Iterator<String> stringIterator = Arrays.asList("England", "India").iterator();
ChainedIterator<Integer> iterator = ChainedIterator.fromIterator(stringIterator)
.map(s -> s.length())
.map(i -> String.valueOf(i))
.map(s -> s.length());
I hope this helps :)
How does method chaining work in Java 8 Comparator?
As you chain both, the compiler cannot infer the type argument of the returned comparator of comparing()
because it depends on the returned comparator of thenComparingInt()
that itself cannot be inferred.
Specify the type in the lambda parameter of comparing()
(or use a method reference) and it solves the inference issue as the returned type of comparing()
could so be inferred. :
Comparator<Squirrel> c = Comparator.comparing((Squirrel s) -> s.getSpecies())
.thenComparingInt(s -> s.getWeight());
Note that specifying the type in the lambda parameter of thenComparingInt()
(or using a method reference) such as :
Comparator<Squirrel> c = Comparator.comparing(s -> s.getSpecies())
.thenComparingInt((Squirrel s) -> s.getWeight());
will not work as the receiver (here the return type of the chained method) is not considered in the inference type computation.
This JDK 8 tutorial/documentation explains that very well :
Note: It is important to note that the inference algorithm uses only
invocation arguments, target types, and possibly an obvious expected
return type to infer types. The inference algorithm does not use
results from later in the program.
Related Topics
Only Using @JSONignore During Serialization, But Not Deserialization
Problems Converting Byte Array to String and Back to Byte Array
Java: How to Split an Arraylist in Multiple Small Arraylists
Why am I Getting Inputmismatchexception
How to Restart a Java Application
Regex to Match Only Commas Not in Parentheses
What Is the Use of Interface Constants
Error: Selection Does Not Contain a Main Type
How to Convert Milliseconds to "Hh:Mm:Ss" Format
Difference Between Paint, Paintcomponent and Paintcomponents in Swing
A Simple Scenario Using Wait() and Notify() in Java
Fastest Way to Write Huge Data in Text File Java
Java Remove Duplicates from an Array
How to Get Vm Arguments from Inside of Java Application
Java Random Always Returns the Same Number When I Set the Seed