在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,其中此模式在任何地方都使用。
更新
我所描述的内容显然是有效的,但我真正想要的是关于这是否普遍可以接受,以及是否存在任何陷阱或相关最佳实践的一些想法。我知道Builder模式,但它比我描述的要多一点 - 正如Josh Bloch描述的那样,有一个关联的静态Builder类用于创建对象。
答案 0 :(得分:96)
这不是一种糟糕的做法。这是一种越来越普遍的做法。如果您不想这样做,大多数语言都不要求您处理返回的对象,因此它不会更改“正常”的setter使用语法,但允许您将setter链接在一起。
这通常称为构建器模式或fluent interface。
在Java API中也很常见:
String s = new StringBuilder().append("testing ").append(1)
.append(" 2 ").append(3).toString();
答案 1 :(得分:81)
总结:
未提及的其他几点:
这违反了每个函数应该做一个(并且只有一个)事情的原则。您可能会或可能不会相信这一点,但在Java中我认为它运作良好。
IDE不会为您生成这些(默认情况下)。
我终于,这是一个真实的数据点。我使用像这样构建的库时遇到了问题。 Hibernate的查询构建器就是现有库中的一个示例。由于Query的set *方法正在返回查询,因此仅通过查看签名如何使用它是不可能的。例如:
Query setWhatever(String what);
它引入了歧义:该方法是否修改了当前对象(您的模式),或者,Query可能是不可变的(非常流行且有价值的模式),并且该方法返回一个新方法。它只是使库更难使用,许多程序员不利用这个功能。如果setter是setter,那么如何使用它会更清楚。
答案 2 :(得分:76)
我更喜欢使用'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!"));
答案 3 :(得分:70)
我认为它没有什么特别的错误,只是风格问题。它在以下情况下很有用:
此方法的替代方案可能是:
如果你只想一次设置一些房产,我会说不值得返回'这个'。如果您以后决定返回其他内容,例如状态/成功指示/消息,那肯定会失败。
答案 4 :(得分:25)
如果您不想从setter返回'this'
但又不想使用第二个选项,则可以使用以下语法设置属性:
list.add(new Employee()
{{
setName("Jack Sparrow");
setId(1);
setFoo("bacon!");
}});
顺便说一句,我认为它在C#中稍微清晰一些:
list.Add(new Employee() {
Name = "Jack Sparrow",
Id = 1,
Foo = "bacon!"
});
答案 5 :(得分:11)
它不仅打破了getter / setter的惯例,还打破了Java 8方法引用框架。 MyClass::setMyValue
是BiConsumer<MyClass,MyValue>
,myInstance::setMyValue
是Consumer<MyValue>
。如果您的setter返回this
,那么它不再是Consumer<MyValue>
的有效实例,而是Function<MyValue,MyClass>
,并且会使用对这些setter的方法引用(假设它们无效)方法)打破。
答案 6 :(得分:7)
我不懂Java,但我已经用C ++完成了。 其他人说它会使线条很长很难读, 但我已经很多次这样做了:
list.add(new Employee()
.setName("Jack Sparrow")
.setId(1)
.setFoo("bacon!"));
这更好:
list.add(
new Employee("Jack Sparrow")
.Id(1)
.foo("bacon!"));
至少,我认为。但是如果你愿意的话,欢迎你给我一个可怕的程序员。而且我不知道你是否被允许在Java中做到这一点。
答案 7 :(得分:6)
因为它不返回void,所以它不再是有效的JavaBean属性setter。如果您是世界上使用可视化“Bean Builder”工具的七个人之一,或使用JSP-bean-setProperty元素的17个人之一,这可能很重要。
答案 8 :(得分:6)
这种被称为“流畅的界面”的方案(双关语)现在变得非常流行。这是可以接受的,但这不是我的一杯茶。
答案 9 :(得分:5)
这根本不是一个坏习惯。但它与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();
答案 10 :(得分:5)
理论上至少 ,它可以通过在调用之间设置错误的依赖关系来破坏JVM的优化机制。
它应该是语法糖,但实际上可以在超级智能Java 43的虚拟机中创建副作用。
这就是为什么我不投票,不要使用它。
答案 11 :(得分:3)
我赞成有“这个”回报的二传手。我不在乎它是否符合豆类标准。对我来说,如果可以使用“=”表达式/语句,那么返回值的setter就可以了。
答案 12 :(得分:3)
Paulo Abrantes提供了另一种使JavaBean setter流畅的方法:为每个JavaBean定义一个内部构建器类。如果你使用的工具会被返回值的setter搞砸,那么Paulo的模式可能有所帮助。
答案 13 :(得分:2)
我以前更喜欢这种方法,但我已经决定反对它。
原因:
我看到的Builder模式不使用setFoo(foo).setBar(bar)约定,而是使用更多foo(foo).bar(bar)。也许正是出于这些原因。
一如既往的味道。我只是喜欢“最不惊喜”的方法。
答案 14 :(得分:2)
此特定模式称为方法链接。 Wikipedia link,这有更多的解释和示例,说明它是如何在各种编程语言中完成的。
P.S:只是想把它留在这里,因为我正在寻找具体的名字。答案 15 :(得分:2)
如果你在整个应用程序中使用相同的约定,那么它似乎很好。
如果你的应用程序的现有部分使用标准约定,那么我会坚持使用它并将构建器添加到更复杂的类中
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;
}
}
答案 16 :(得分:1)
是的,我认为这是一个好主意。
如果我可以添加一些东西,那么这个问题:
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;
}
}
答案 17 :(得分:1)
一般来说这是一个很好的做法,但你可能需要set-type函数使用Boolean类型来确定操作是否成功完成,这也是一种方法。一般来说,没有教条可以说这是好的还是床,当然是来自这种情况。
答案 18 :(得分:1)
乍一看:“可怕!”。
进一步思考
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
实际上比
更不容易出错Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);
非常有趣。将想法添加到工具包......
答案 19 :(得分:0)
这可能不太可读
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
或者
list.add(new Employee()
.setName("Jack Sparrow")
.setId(1)
.setFoo("bacon!"));
这比以下方式更具可读性:
Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!"));
list.add(employee);
答案 20 :(得分:0)
来自声明
list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));
我看到了两件事
1)毫无意义的陈述。 2)缺乏可读性。
答案 21 :(得分:0)
我已经让我的setter工作了很长一段时间,唯一真正的问题是库坚持使用严格的getPropertyDescriptors来获取bean读取器/编写器bean访问器。在这些情况下,你的java&#34; bean&#34;不会有你期望的写作。
例如,我没有对它进行过测试,但是当我从json / maps创建java对象时,杰克逊不会认识到这些是不知道的,我也不会感到惊讶。我希望我错了(我会尽快测试)。
事实上,我正在开发一个轻量级的SQL中心ORM,我必须将一些代码beyong getPropertyDescriptors添加到返回此功能的已识别的setter中。
答案 22 :(得分:0)
很久以前回答,但我的两分钱......很好。我希望更频繁地使用这种流畅的界面。
重复'factory'变量不会在下面添加更多信息:
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...
这是更清洁,imho:
ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...
当然,作为已经提到的答案之一,必须调整Java API以在某些情况下正确执行此操作,例如继承和工具。
答案 23 :(得分:0)
如果可用,最好使用其他语言结构。例如,在Kotlin中,您可以使用 with , apply 或 let 。如果使用这种方法,您将不会需要从您的setter返回实例。
此方法允许您的客户端代码为:
以下是一些例子。
val employee = Employee().apply {
name = "Jack Sparrow"
id = 1
foo = "bacon"
}
val employee = Employee()
with(employee) {
name = "Jack Sparrow"
id = 1
foo = "bacon"
}
val employee = Employee()
employee.let {
it.name = "Jack Sparrow"
it.id = 1
it.foo = "bacon"
}
答案 24 :(得分:0)
如果我正在编写API,我会使用“return this”来设置仅设置一次的值。如果我有任何其他值,用户应该能够更改,我改为使用标准的void setter。
然而,这实际上是一个偏好问题,在我看来,链接制定者确实看起来很酷。
答案 25 :(得分:0)
我同意所有发布者的观点,声称这违反了JavaBeans规范。有理由保留它,但我也感到使用了这个构建器模式(已暗示)有其应有的地位。只要不是在所有地方都使用它,就应该可以接受。对我来说,“ It's Place”是对“ build()”方法的调用的终点。
当然,还有其他方法可以设置所有这些内容,但是这样做的好处是它避免了1)多参数公共构造函数和2)部分指定的对象。在这里,您让构建器收集了所需的内容,然后在最后调用其“ build()”,这可以确保未构建部分指定的对象,因为该操作的可见性不及公共。替代方案是“参数对象”,但是恕我直言,只是将问题推回了一个层次。
我不喜欢多参数构造函数,因为它们使传入许多相同类型的参数的可能性更大,这使得将错误的参数传递给参数更加容易。我不喜欢使用很多设置器,因为可以在完全配置对象之前使用该对象。此外,通过“ build()”方法可以更好地满足基于先前选择的默认值的概念。
简而言之,如果使用得当,我认为这是一个好习惯。
答案 26 :(得分:-4)
坏习惯: 一套二传手套装 getter get
如何明确声明一个方法,为U
做setPropertyFromParams(array $hashParamList) { ... }