One of the first things that bugged me about Java when I first started to use it was a small thing. Why didn’t add() return the list (or Vector back then) so I could chain the calls together.
I wanted to be able to:
Vector v = new Vector().add(”foo”).add(”bar);
The same goes for having chained set* methods:
foo.setA(a).setB(b)
We see this used, especially in certain packages such as Hibernate.
One feature that I liked in Perl was the ability to know about the callers context:
my $scalar = @list; # or function which returns a list
is very different to:
my ($element) = @list; # or function which returns a list
Sometimes I would love to do the same in this world and say “If the caller of my set method wants the object back, give it, else we are in void world and that is fine too”.
Of course, I don’t think this would happen :)
Another way to make me a little more happy would be to give me named parameters, but that doesn’t give me the same power.
April 12th, 2005 at 1:44 pm
Interestingly, the first feature would give you the second one for free.
new Window().color(RED).width(100).height(200);
–
Ced
April 12th, 2005 at 1:57 pm
I love “chained methods” (that’s how I tend to call them), and that’s also why I love jMock so much: for its readability! Look at that:
mockSubscriber.expects(once()).method(”receive”).with( same(message) );
April 12th, 2005 at 1:59 pm
I’ve been working on a JDK 5 library that lets you do something like what you’re talking about. I started with the notion that Iterable was a good thing, but Stream extends Iterable could be much better. Note that as calls are made in these examples the full type of the stream is preserved…if you create a subclass of the stream types you still get to call your subclass’s methods.
Hopefully it’ll get to the point where I can put it out somewhere. Right now I use it to do lots of different things; SQL-like data selection, reflection over object graphs, etc…
A couple of call examples follow.
public void testPipeline() {
Pipeline pipeStart = new Pipeline();
Stream> is = pipeStart
.shuffle()
.first(5)
.sort()
.transform(new Transform(){
public String transform(Integer from)
{
return “Hello ” + from;
}})
.end(pipeStart);
is = is.insert(Integers.random(25));
is.dump(”Hello “);
System.out.println(is.getClass().getName());
//.dump(”Hello”);
}
public void testAugment2() {
randomEmployees(100)
.where(new Transform() {
public Boolean transform(Employee from) {
return from.getName().startsWith(”Kurt”);
}})
.dump(”Employees starting with Kurt”)
.transform(new Transform>() {
public Tuple2 transform(Employee from) {
return Tuple.make(from, from.hashCode());
} })
.dump(”Employee–hashcode tuples”);
}
April 12th, 2005 at 2:00 pm
Let this be a lesson to you, young man. Do not attempt to put type annotations into comment examples ;)
April 12th, 2005 at 3:42 pm
I think that chained method calls are ugly, confusing, and that they pollute the simplicity of Java.
April 12th, 2005 at 10:16 pm
That Keith dude is right on. Your complaint is with the language, not with the return value declaration! What you really are asking for is a “with” clause. Peace.
April 13th, 2005 at 1:23 am
java.nio has chained methods.
In the right API, and used in moderation, they can be cool. But Keith is right that they can easily be abused and lead to ugly, confusing code.
April 13th, 2005 at 3:48 am
Can I suggest looking at the Law of demeter? It’s been around a while: http://c2.com/cgi/wiki?LawOfDemeter
It’s one of those OO things.
Using “it’s in the Java API!” as justification for something has left us with a large amount of code that’s as nice to use as the calendar API. The JMock example is something else entirely – there it’s using the syntax of Java to produce readable assertions – it’s an allowable exception (in my opinion) to the law of demeter.
April 13th, 2005 at 11:44 am
This is not a good thing. Get a NPE in this sentence and try to debug it.
Java is not english, don’t try to make it so.
mockSubscriber.expects(once()).method(”receive”).with( same(message) );
April 14th, 2005 at 11:04 am
Just change it to
mockSubscriber.expects(once())
  .method(”receive”).
  .with( same(message) );
The line number of the exception will reflect the line call. Also, the exception itself will reflect which method call; either expects, once() method, with, same, or the calling method.
I use “chained calls” all the time with string buffers.
StringBuffer buf = new StringBuffer();
buf.append(”There have been “)
  .append(count)
  .append(” errors in the “)
  .append(systemName)
  .append(” system in the last “)
  .append(timeDelta).append(” “)
  .append(timeGranularity);
April 15th, 2005 at 3:40 pm
I think chained-methods are nice and useful, but they give the impression that the object is immutable — that it has to return a new instance rather than perform a mutation. The last thing you want is for clients not to realize they are mutating an object when they are. I would recommend using chained methods if and only if the object is immutable.
If there’s one exception, it’s StringBuffer, because (1) it’s used so frequently; (2) it so significantly reduces the amount of code; and (3) it’s very well known that it’s mutable.
June 2nd, 2006 at 4:49 pm
captive