让java中的setter返回“this”是好还是坏?
public Employee setName(String name){
this.name = name;
return this;
}
这个模式很有用,因为你可以像这样设置链:
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
而不是这样:
Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);
...但这有点违背标准惯例。我认为这是值得的因为它可以让setter做一些其他有用的事情。我曾在一些地方看到过这种模式(例如JMock、JPA),但它似乎并不常见,而且通常只用于定义良好的api,在这些api中这种模式随处可见。
更新:
我所描述的显然是有效的,但我真正想要的是一些关于这是否被普遍接受的想法,以及是否存在任何陷阱或相关的最佳实践。我知道Builder模式,但它比我所描述的要复杂一些——正如Josh Bloch所描述的,有一个用于对象创建的相关静态Builder类。
总结:
它被称为“流畅接口”或“方法链接”。
这不是“标准”Java,尽管你现在看到的越来越多(在jQuery中工作得很好)
它违反了JavaBean规范,因此它将与各种工具和库分开,特别是JSP构建器和Spring。
它可能会阻止JVM通常会做的一些优化
一些人认为它能清理代码,另一些人认为它“可怕”
还有一些没有提到的要点:
This violates the principal that each function should do one (and only one) thing. You may or may not believe in this, but in Java I believe it works well.
IDEs aren't going to generate these for you (by default).
I finally, here's a real-world data point. I have had problems using a library built like this. Hibernate's query builder is an example of this in an existing library. Since Query's set* methods are returning queries, it's impossible to tell just by looking at the signature how to use it. For example:
Query setWhatever(String what);
It introduces an ambiguity: does the method modify the current object (your pattern) or, perhaps Query is really immutable (a very popular and valuable pattern), and the method is returning a new one. It just makes the library harder to use, and many programmers don't exploit this feature. If setters were setters, it would be clearer how to use it.
我更喜欢使用“with”方法:
public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
setFoo(foo);
return this;
}
因此:
list.add(new Employee().withName("Jack Sparrow")
.withId(1)
.withFoo("bacon!"));
警告:此withX语法通常用于为不可变对象提供“setter”,因此这些方法的调用者可能合理地期望它们创建新对象,而不是改变现有实例。也许更合理的措辞应该是:
list.add(new Employee().chainsetName("Jack Sparrow")
.chainsetId(1)
.chainsetFoo("bacon!"));
使用chainsetXyz()命名约定,几乎每个人都应该满意。
是的,我认为这是个好主意。
如果我能补充点什么,关于这个问题:
class People
{
private String name;
public People setName(String name)
{
this.name = name;
return this;
}
}
class Friend extends People
{
private String nickName;
public Friend setNickName(String nickName)
{
this.nickName = nickName;
return this;
}
}
这是可行的:
new Friend().setNickName("Bart").setName("Barthelemy");
Eclipse将不接受这一点!:
new Friend().setName("Barthelemy").setNickName("Bart");
这是因为setName()返回People而不是Friend,并且没有PeoplesetNickName。
我们如何编写setter来返回SELF类而不是类名呢?
这样就可以了(如果SELF关键字存在的话)。这真的存在吗?
class People
{
private String name;
public SELF setName(String name)
{
this.name = name;
return this;
}
}
很久以前的答案,但我的两分钱…这是很好的。我希望这个流畅的界面被更多地使用。
重复'factory'变量不会在下面添加更多信息:
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...
恕我直言,这个更干净:
ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...
当然,正如前面提到的答案之一,Java API必须进行调整,以便在某些情况下(如继承和工具)正确执行此操作。
这一点也不坏。但是它与JavaBeans Spec不兼容。
有很多规范依赖于这些标准访问器。
你总能让它们共存。
public class Some {
public String getValue() { // JavaBeans
return value;
}
public void setValue(final String value) { // JavaBeans
this.value = value;
}
public String value() { // simple
return getValue();
}
public Some value(final String value) { // fluent/chaining
setValue(value);
return this;
}
private String value;
}
现在我们可以一起用了。
new Some().value("some").getValue();
下面是另一个版本的不可变对象。
public class Some {
public static class Builder {
public Some build() { return new Some(value); }
public Builder value(final String value) {
this.value = value;
return this;
}
private String value;
}
private Some(final String value) {
super();
this.value = value;
}
public String getValue() { return value; }
public String value() { return getValue();}
private final String value;
}
现在我们可以这么做了。
new Some.Builder().value("value").build().getValue();
我同意所有声称这破坏了JavaBeans规范的帖子。有理由保留这一点,但我也觉得使用这个构建器模式(上面提到的)有它的一席之地;只要不是到处都用,就应该是可以接受的。对我来说,“It’s Place”的终点是对“build()”方法的调用。
There are other ways of setting all these things of course, but the advantage here is that it avoids 1) many-parameter public constructors and 2) partially-specified objects. Here, you have the builder collect what's needed and then call its "build()" at the end, which can then ensure that a partially-specified object is not constructed, since that operation can be given less-than-public visibility. The alternative would be "parameter objects", but that IMHO just pushes the problem back one level.
我不喜欢多形参构造函数,因为它们更有可能传入大量相同类型的实参,从而更容易将错误的实参传递给形参。我不喜欢使用大量的setter,因为对象可以在完全配置之前使用。此外,使用“build()”方法更好地实现基于先前选择的默认值的概念。
总之,我认为这是一个很好的实践,如果使用得当。