Method Chaining in Java [closed]

2019-01-16 13:04发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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 :)



回答4:

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.



回答5:

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! :-)



回答6:

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