Is it bad practice to make a setter return “this”?

2020-01-25 12:18发布

Is it a good or bad idea to make setters in java return "this"?

public Employee setName(String name){
   this.name = name;
   return this;
}

This pattern can be useful because then you can chain setters like this:

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

instead of this:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

...but it sort of goes against standard convention. I suppose it might be worthwhile just because it can make that setter do something else useful. I've seen this pattern used some places (e.g. JMock, JPA), but it seems uncommon, and only generally used for very well defined APIs where this pattern is used everywhere.

Update:

What I've described is obviously valid, but what I am really looking for is some thoughts on whether this is generally acceptable, and if there are any pitfalls or related best practices. I know about the Builder pattern but it is a little more involved then what I am describing - as Josh Bloch describes it there is an associated static Builder class for object creation.

27条回答
走好不送
2楼-- · 2020-01-25 13:04

It not only breaks the convention of getters/setters, it also breaks the Java 8 method reference framework. MyClass::setMyValue is a BiConsumer<MyClass,MyValue>, and myInstance::setMyValue is a Consumer<MyValue>. If you have your setter return this, then it's no longer a valid instance of Consumer<MyValue>, but rather a Function<MyValue,MyClass>, and will cause anything using method references to those setters (assuming they are void methods) to break.

查看更多
【Aperson】
3楼-- · 2020-01-25 13:05

I used to prefer this approach but I have decided against it.

Reasons:

  • Readability. It makes the code more readable to have each setFoo() on a separate line. You usually read the code many, many more times than the single time you write it.
  • Side effect: setFoo() should only set field foo, nothing else. Returning this is an extra "WHAT was that".

The Builder pattern I saw do not use the setFoo(foo).setBar(bar) convention but more foo(foo).bar(bar). Perhaps for exactly those reasons.

It is, as always a matter of taste. I just like the "least surprises" approach.

查看更多
家丑人穷心不美
4楼-- · 2020-01-25 13:06

If you use the same convention in whole applicaiton it seems fine.

On the oher hand if existing part of your application uses standard convention I'd stick to it and add builders to more complicated classes

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}
查看更多
登录 后发表回答