While answering a few questions on here earlier and from some work I have been doing lately I have been wondering why Java does not support method chaining on its built in classes.
If I were to create a Car
class for example, I could make it chainable by reutrning this
instead of void as follows:
public class Car {
private String make;
public Car setMake(String make) {
this.make = make;
return this;
}
}
Is there any particular reason why the built in libraries don't tend to do things this way? Is there a downside to method chaining?
I may have overlooked something which would explain the lack of method chaining however any setter method that returns void by default should return a reference to this (at least in my eyes it should). This would make situations like the following much cleaner.
container.add((new JLabel("label text")).setMaximumSize(new Dimension(100,200)));
rather than the more long winded: Note: It would not stop you from coding this way if you wished.
JLabel label = new JLabel("label text");
label.setMaximumSize(new Dimension(100,200));
container.add(label);
I would be very interested to hear the reasons behind this decision, If I had to guess it would be that there is an overhead associated with this and so should only be used when needed.
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.
Another reason I can think of is performance, or more precisely: don't pay for something you don't use. return this
after each and every method isn't very costly, but still requires few extra CPU cycles and one CPU registry.
There was even an idea to add implicit return this
to every method declaring void
return value but it was rejected.
Although we can but guess as to the real reason, one of them could be that strictly speaking it's not very OO.
If you view methods as actions that represent an action of the modelled world, method chaining doesn't really fit into that picture.
Another reason could be the chaos that might ensue when you're trying to override a chained method. Imagine this situation:
class Foo {
Foo chainedMethod() {...}
}
class Bar extends Foo {
Foo chainedMethod() {...} //had to return Foo for Java versions < 1.5
void doStuff();
}
So when you're trying to do new Bar().chainedMethod().doStuff()
, it won't compile. And ((Bar)new Bar().chainedMethod()).doStuff()
doesn't look very good, does it :)
Method chaining is typically used with the Builder pattern, and you can see this in the StringBuilder
class. Outside of that, method chaining can make the command and creation separation less clear, for instance should repaint()
also be part of the fluent interface? Then I could have:
container.add(label).repaint().setMaximumSize(new Dimension(100,200)));
and suddenly the order of my calls becomes important which can get hidden in the method chaining.
You are really asking about the Law of Demeter
"Only talk to your immediate friends"
Note that the above link is a wonderful resource but you can spend a long time there and not get an answer you were looking for! :-)
The reason behind not implementing method chaining would be some of the following:
The biggest problem for Method Chaining is the finishing problem.
While there are workarounds, usually if you run into this you're
better off using a Nested Function. Nested Functions are also a better
choice if you are getting into a mess with Context Variables.
There's no guarantee that an object out of the chain will actually return a valid,
non-null object. Also, debugging this style of code is often a lot
harder since many *IDE*s will not evaluate the method call at debug
time as an object that you can inspect
It can get confusing when you're calling routines to other classes to
pass arguments to one of the chaining methods, when there are a lot
of parameters to pass mainly because the lines get very long
There is also a performance issue, which will work hard on memory