让二传手返回“ this”是否是错误的做法?


249

使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);

...但是有点违反标准惯例。我想这可能是值得的,因为它可以使设置者做其他有用的事情。我已经看到此模式在某些地方使用过(例如JMock,JPA),但这似乎并不常见,并且仅用于定义非常明确的API,该模式随处可见。

更新:

我所描述的内容显然是有效的,但是我真正要寻找的是一些关于是否可以普遍接受以及是否存在陷阱或相关最佳实践的想法。我知道Builder模式,但是它比我描述的要复杂得多-正如Josh Bloch所描述的那样,有一个关联的静态Builder类用于对象创建。


1
由于前一段时间我已经看到过这种设计模式,所以我会尽可能做到这一点。如果某个方法不需要明确地返回某项来完成其工作,则它现在会返回this。有时,我什至更改函数,以便它不返回值,而是对对象的成员进行操作,以使我能够做到这一点。太好了 :)
Inversus

4
对于返回自己和构建器的伸缩式安装程序,我更喜欢使用withName(String name)而不是setName(String name)。正如您所指出的那样,二传手的一种常见做法和期望是返回无效值。“非标准”设置员在现有框架(例如JPA实体管理器,Spring等)中可能无法正常工作。
oᴉɹǝɥɔ2016年

请在每次调用之前引入换行符:)并配置您的IDE或在不遵守此要求的情况下获得一个合适的IDE。
MauganRa '17

广泛使用的框架(例如,Spring和Hibernate)将严格(至少曾经使用过)遵守无效者惯例
Legna

Answers:


83

我不认为这有什么特别的问题,这只是风格问题。在以下情况下很有用:

  • 您需要一次设置多个字段(包括在构造中)
  • 您知道在编写代码时需要设置哪些字段,以及
  • 您要为哪些字段设置许多不同的组合。

此方法的替代方法可能是:

  1. 一个大型构造函数(缺点:您可能传递大量的null或默认值,并且很难知道哪个值对应什么)
  2. 几个重载的构造函数(缺点:一旦有多个,就会变得笨拙)
  3. 工厂/静态方法(缺点:与重载的构造函数相同-一旦存在过多,就会变得笨拙)

如果您一次只设置几个属性,我会说不值得返回“ this”。如果您以后决定返回其他内容,例如状态/成功指示符/消息,它肯定会下降。


1
好吧,按照惯例,通常您不会从设置器中返回任何东西。
刘坚

17
也许不是从一开始,但二传手不一定保持其初衷。曾经是变量的变量可能会变成包含多个变量或具有其他副作用的状态。如果故障对于异常异常太常见,则某些设置器可能返回先前的值,而其他设置器可能返回故障指示符。但是,这又引出了另一个有趣的观点:如果您使用的工具/框架在有返回值的情况下无法识别您的设置器,该怎么办?
汤姆·克里夫特

12
@Tom好点,这样做违反了getter和setter的“ Java bean”约定。
安迪·怀特2009年

2
@TomClift违反“ Java Bean”约定是否会引起任何问题?是使用“ Java Bean”约定查看返回类型或仅查看方法参数和方法名称的库。
西奥布里斯科

3
这就是为什么存在Builder模式的原因,setter不应返回任何内容,而是在需要时看起来更好并且需要较少代码的情况下创建一个builder :)
RicardoE 2015年

106

这不是一个坏习惯。这是越来越普遍的做法。如果您不想,大多数语言不需要您处理返回的对象,这样它就不会更改“正常”的setter使用语法,但是允许您将setter链接在一起。

这通常称为构建器模式或流利的界面

在Java API中也很常见:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();

26
它通常使用的建设者,但我不会说“这是...叫做Builder模式”。
劳伦斯·贡萨尔维斯

10
让我感到有趣的是,流畅接口的一些基本原理是它们使代码更易于阅读。我可以看到它写起来更方便,但是有点难于阅读。这是我唯一真正的分歧。
布伦特写代码

30
它也被称为火车残骸反模式。问题是,当空指针异常堆栈跟踪包含这样的行时,您不知道哪个调用返回空值。这并不是说应该不惜一切代价避免链接,而是要提防不良的库(尤其是自制的库)。
ddimitrov

18
@ddimitrov aslong你限制它返回这个它永远不会是一个问题(仅第一invokation可能抛出NPE)
斯特凡

4
假设您将换行符和缩进放在否则会损害可读性的情况下,编写和阅读起来会更容易!(因为它避免了像bla.foo.setBar1(...) ; bla.foo.setBar2(...)编写代码时那样重复代码的多余混乱bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)(不能在这样的SO注释中使用换行符:-(...希望您能理解10个这样的setter或更复杂的调用))
Andreas Dietrich

90

总结一下:

  • 它称为“流利接口”或“方法链接”。
  • 这不是“标准” Java,尽管最近您确实看到了更多(在jQuery中效果很好)
  • 它违反了JavaBean规范,因此将与各种工具和库一起破坏,尤其是JSP构建器和Spring。
  • 它可能会阻止JVM通常会进行的某些优化
  • 有些人认为它清理了代码,另一些人认为它“很糟糕”

未提及的其他几点:

  • 这违反了每个函数都应该做一件(并且只能一件)事情的原则。您可能会或可能不会相信这一点,但是我相信在Java中它会很好地工作。

  • IDE不会为您生成这些(默认情况下)。

  • 我终于有了一个真实的数据点。使用这样的库时,我遇到了问题。Hibernate的查询生成器是现有库中的一个示例。由于Query的set *方法正在返回查询,因此仅通过查看签名来说明如何使用是不可能的。例如:

    Query setWhatever(String what);
  • 它引入了一个歧义:方法是修改当前对象(您的模式),还是Query确实是不可变的(一种非常流行且有价值的模式),并且该方法返回了一个新对象。这只会使该库更难使用,并且许多程序员不会利用此功能。如果二传手是二传手,那么使用起来会更清楚。


5
顺便说一句,它是“流畅的”,而不是“流畅的” ...因为它可以让您构造一系列方法调用,例如口头语言。
刘坚

1
关于不变性的最后一点非常重要。最简单的示例是字符串。Java开发人员期望在String上使用方法时,它们将获得全新的实例,而不是相同但经过修改的实例。使用流畅的界面时,方法文档中必须提到返回的对象是“ this”而不是新实例。
MeTTeO 2015年

7
尽管我总体上同意,但是我不同意这违反了“只做一件事情”的原则。归来this几乎不是一门复杂的火箭科学。:-)
user949300 '16

另外一点:它还违反了命令查询分离原则。
Marton_hun

84

我更喜欢使用'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语法通常用于为不可变对象提供“设置器”,因此这些方法的调用者可能会合理地期望它们创建新对象,而不是改变现有实例。也许更合理的措辞应该是这样的:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

使用chainsetXyz()命名约定,几乎每个人都应该感到高兴。


18
+1有趣的约定。我不会在我自己的代码中采用它,因为似乎现在每个类字段都必须有一个get,一个set和一个with。不过,它仍然是一个有趣的解决方案。:)
Paul Manta

1
这取决于您致电二传手的频率。我发现,如果这些设置器被调用很多,那么添加它们就值得额外麻烦,因为它可以简化其他地方的代码。YMMV
Qualidafial 2011年

2
而且,如果您将此添加到Project Lombok@Getter/@Setter注释中,那将非常适合链接。或者,您可以使用很多类似JQuery和Javascript恶魔使用的Kestrel组合器(github.com/raganwald/Katy)。
Ehtesh Choudhury 2012年

4
@ AlikElzin-kilaka其实我只是注意到java.time一成不变的类在Java中使用8这种模式,如LocalDate.withMonth,withYear等
qualidafial

4
with前缀是不同的约定。以@qualidafial为例。前缀为的方法with不应返回this,而应返回一个新实例(如当前实例),但是with这种变化。当您希望对象不可变时,执行此操作。因此,当我看到带有前缀的方法时,with我会假设我将获得一个新对象,而不是同一对象。
tempcke

26

如果您不想'this'从设置器中返回但又不想使用第二个选项,则可以使用以下语法来设置属性:

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

顺便说一句,我认为它在C#中稍微干净一些:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});

16
双括号初始化可能会导致equals出现问题,因为它会创建一个匿名内部类;请参阅c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H,2009年

@Csaba_H显然,问题出在弄乱该equals方法的人的过错。equals如果您知道自己在做什么,则有非常干净的方法来处理匿名类。
AJMansfield 2014年

为此创建一个新的(匿名)类?对于每个实例?
user85421 '19

11

它不仅打破了getters / setter的约定,而且打破了Java 8方法参考框架。MyClass::setMyValueBiConsumer<MyClass,MyValue>,并且myInstance::setMyValueConsumer<MyValue>。如果您有setter return this,则它不再是的有效实例Consumer<MyValue>,而是一个Function<MyValue,MyClass>,并且会导致使用对这些setter的方法引用(假设它们是无效方法)导致任何中断。


2
如果Java有某种方法可以按返回类型(而不只是JVM)重载,那就太好了。您可以轻松绕过许多重大更改。
Adowrath

1
您始终可以定义一个功能接口,以同时扩展这两个功能,Consumer<A>Function<A,B>提供的默认实现void accept(A a) { apply(a); }。这样就可以轻松地将其用作任何一种,并且不会破坏任何需要特定格式的代码。
史蒂夫·K

啊!这是完全错误的!这称为无效兼容性。返回值的设置器可以充当使用者。ideone.com/ZIDy2M-
迈克尔

8

我不懂Java,但是我已经用C ++做到了。其他人已经说过,这会使行很长,而且很难阅读,但我已经做过很多次了:

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

这样更好:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

至少,我认为。但是,如果您愿意,欢迎您对我投反对票,并称我为糟糕的程序员。而且我不知道您是否甚至可以使用Java来做到这一点。


“甚至更好”并不适合现代IDE的可用格式源代码功能。不幸。
托尔比约恩Ravn的安德森

您可能是对的...我使用的唯一自动格式化程序是emacs的自动缩进。
卡森·迈尔斯,

2
在链中的每个方法调用之后,都可以使用简单的//强制执行源代码格式化程序。它使您的代码有些难看,但使水平的垂直系列语句重新格式化的程度不高。
qualidafial 2010年

@qualidafial //如果您将IDE配置为不连接已包装的行(例如,Eclipse>属性> Java>代码样式>格式器>换行>永不连接已包装的行),则不需要每种方法。
DJDaveMark '19

6

这种称为“流利接口”的方案(双关语意)现在正变得非常流行。可以接受,但实际上不是我的茶。


6

因为它不会返回void,所以它不再是有效的JavaBean属性设置器。如果您是使用可视化“ Bean Builder”工具的七个人之一,还是使用JSP-bean-setProperty元素的十七个之一,那么这可能很重要。


如果使用像Spring这样的支持bean的框架,这也很重要。
ddimitrov

6

至少从理论上讲,它可以通过在调用之间设置错误的依赖关系来破坏JVM的优化机制。

它应该是语法糖,但实际上会在超智能Java 43的虚拟机中产生副作用。

这就是为什么我不投票,不要使用它。


10
有趣的...您能对此做些扩展吗?
刘坚

3
只要考虑一下超标量处理器如何处理并行执行。尽管程序员已知道执行第二种set方法的对象取决于第set一种方法。
玛丽安

2
我还是不跟随。如果先用两个单独的语句设置Foo,然后设置Bar,则要为其设置Bar的对象与要为其设置Foo的对象具有不同的状态。因此,编译器也无法并行化这些语句。至少,如果不引入不必要的假设,我不知道怎么办(由于我对此一无所知,因此我不会否认Java 43实际上只在一种情况下进行了并行化,而没有在其他情况下进行并行化并引入了一种情况下没有根据的假设,而另一种情况下则没有)。
masonk 2011年

12
如果您不知道,请进行测试。 -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining java7 jdk绝对内联链式方法,并且执行与将void setter标记为hot并内联它们相同的迭代次数。可能您低估了JVM操作码修剪算法的功能;如果知道您要返回此代码,它将跳过jrs(java return语句)操作码,并将其保留在堆栈中。
阿贾克斯

1
安德烈亚斯,我同意,但是当您一层又一层的低效率代码时,就会出现问题。您有99%的时间应该为清楚起见而编写代码,这在这里经常被提及。但是有时候,您需要变得现实,并利用多年的经验来从一般的架构角度进行过早的优化。
LegendLength

6

这根本不是一个坏习惯。但这与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();

2
我的编辑被拒绝,但是您的Builder示例不正确。首先,.value()不返回任何内容,甚至不设置该some字段。其次,你应该在保障和组添加somenull在建(),所以Some是真正的不可变的,否则你可以调用builder.value()同一生成器实例一次。最后,是的,您有一个构建器,但是您Some仍然有一个公共构造器,这意味着您不公开倡导使用该构建器,即,用户除了尝试或搜索设置自定义方法之外,对它一无所知。value完全没有
Adowrath

@Adowrath如果答案不正确,您应该编写自己的答案,而不要尝试编辑其他人的形状
CalvT

1
@JinKwon太好了。谢谢!很抱歉,如果我以前看起来很粗鲁。
Adowrath

1
@Adowrath,请随时提出任何其他增强建议。仅供参考,我不是拒绝您编辑的人。:)
晋权

1
我知道我知道。^^并且感谢新版本,它现在提供了一个真正可变的“ Immutable-Some”构建器。与我的编辑尝试相比,这是一个更聪明的解决方案,相比之下,我的编辑使代码混乱。
Adowrath

4

如果您在整个应用程序中使用相同的约定,那似乎很好。

另一方面,如果您的应用程序的现有部分使用标准约定,我会坚持使用它,并将构建器添加到更复杂的类中

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;
    }
}

1
这正是Builder模式旨在解决的问题。它不会破坏Java 8中的lambda,不会破坏挑剔的JavaBeans工具,并且不会在JVM中引起任何优化问题(因为该对象仅在实例化期间存在)。它也解决了您根本不使用构建器而导致的“过多的构造器”问题,以及消除了双括号匿名类的堆污染。
ndm13

有趣的一点-我注意到,如果您的课程中几乎没有什么是不变的(例如,高度可配置的GUI),那么您可能会完全避免使用Builder。
菲利普·金

4

Paulo Abrantes提供了另一种使JavaBean setter变得流畅的方法:为每个JavaBean定义一个内部构建器类。如果您使用的工具会受到返回值的设置器的困扰,那么Paulo的模式可能会有所帮助。


2
@ cdunn2001,使用
桑格

3

我支持有“ this”回报的二传手。我不在乎它是否不符合Bean要求。对我来说,如果可以使用“ =”表达式/语句,则返回值的设置方法就可以了。


2

我曾经更喜欢这种方法,但是我决定反对。

原因:

  • 可读性。将每个setFoo()放在单独的行中可使代码更具可读性。与单次编写代码相比,您通常阅读代码的次数要多得多。
  • 副作用:setFoo()应该只设置字段foo,除此之外别无其他设置。返回这是一个额外的“那是什么”。

我看到的Builder模式不使用setFoo(foo).setBar(bar)约定,而是使用更多foo(foo).bar(bar)。也许正是出于这些原因。

一如既往地关乎口味。我只是喜欢“最少的惊喜”方法。


2
我同意副作用。传回东西的安装员违反了他们的名字。您正在设置foo,但是又得到了一个对象?这是新对象还是我改变了旧对象?
crunchdog

2

这种特定的模式称为方法链接。Wikipedia链接,其中提供了更多解释和使用各种编程语言进行操作的示例。

PS:只是想把它留在这里,因为我一直在寻找具体的名字。


1

乍一看:“可怕!”。

进一步思考

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

实际上比以下情况更容易出错

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

非常有趣。在工具包中添加想法...


1
不,还是很恐怖。从维护的角度来看,后者更好,因为它更易于阅读。此外,自动代码检查器(如CheckStyle)默认情况下会将行强制为80个字符-无论如何代码都会被换行,从而增加了可读性/维护性问题。最后-它是Java;无论如何,如果将所有内容都编译为字节代码,则将所有内容都写在一行上是没有好处的。
OMG小马,

1
就个人而言,我认为前者更易于阅读,尤其是如果您以这种方式创建多个对象时。
刘坚

@Ken:编写一个方法。流畅地写一份;另一个副本。现在,将这两个副本交给几个人,并询问他们觉得哪一个更容易阅读。读取速度更快,编码速度更快。
OMG小马,

像大多数工具一样,它很容易过度使用。JQuery围绕这种技术,因此容易出现长调用链,我发现这实际上会削弱可读性。
staticsan

2
如果每个点之前都有换行并缩进,那就没问题了。然后,它与第二个版本一样可读。实际上更多,因为list一开始没有多余的内容。
MauganRa '17

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;
    }
}

1
除了Eclipse之外,还有其他一些Java编译器不接受:)。基本上,您正在运行Java类型系统(它是静态的,而不像某些脚本语言一样是动态的)的家禽:您必须先将setName()中的内容转换为Friend,然后才能在其上设置setNickName()。因此,不幸的是,对于继承层次结构,这消除了许多可读性优点,并使这种原本可能有用的技术不那么有用。
Cornel Masson 2012年

5
使用泛型。class Chainable <自我扩展Chainable> {public Self doSomething(){return(Self)this;}}从技术上讲,它不是类型安全的(如果您使用无法区分大小写的Self类型实现该类,它将进行类型转换),但是是正确的语法,子类将返回自己的类型。
阿贾克斯

另外:Baptiste使用的这种“ SELF”被称为一种自我类型,在许多语言中都不存在(Scala实际上是我现在唯一想到的一种语言),正如Ajax对Generics的使用就是所谓的“好奇地”。重复模板模式”,它试图解决缺少自我类型的问题,但是它也有一些缺点:( from class C<S extends C<S>>,它比普通类型更安全。a S extends C)如果A extends BB extends C<B>,并且您有A,则您只知道a除非A覆盖每个方法,否则返回B。b)您不能表示本地的非原始C<C<C<C<C<...>>>>>
Adowrath

1

通常,这是一个好习惯,但是您可能需要使用布尔类型的set-type函数来确定操作是否成功完成,这也是一种方法。通常,没有教条可以说这是件好事或卧床不起,这当然取决于情况。


2
如何使用异常来指示错误情况?正如许多C程序员痛苦地学习到的那样,错误代码很容易被忽略。异常会使堆栈堆积到可以处理的程度。
ddimitrov

通常最好使用异常,但是当您不能使用异常时,错误代码也很有用。
Narek

1
@Narek,您好,也许您可​​以详细说明在什么情况下最好在setter中使用错误代码,而不是使用异常,为什么?
ddimitrov '18年

0

从声明

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

我看到两件事

1)毫无意义的陈述。2)缺乏可读性。


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

8
如果您不尝试将所有代码都放在一行上,我认为这是很容易理解的。
刘坚2010年

0

我已经进行了一段时间的设置,唯一真正的问题是那些使用严格的getPropertyDescriptors来获取bean读/写bean访问器的库。在这些情况下,您的Java“ bean”将不会具有您所期望的扭曲。

例如,我没有确定要进行测试,但是当Jackson从json / maps创建Java对象时,Jackson不会将它们识别为setter不会令我感到惊讶。我希望这是错误的(我会尽快对其进行测试)。

实际上,我正在开发一个以SQL为中心的轻量级ORM,并且必须在getPropertyDescriptors之间添加一些代码,以将其返回给已识别的setter。


0

很早以前就回答了,但是我的两分钱。我希望这个流利的界面得到更多的使用。

重复'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进行调整,以在某些情况下正确执行此操作,例如继承和工具。


0

如果可用,最好使用其他语言构造。例如,在Kotlin中,您可以使用withapplylet。如果使用这种方法,则实际上不需要从设置器返回实例。

这种方法允许您的客户端代码为:

  • 不管返回类型
  • 易于维护
  • 避免编译器副作用

这里有些例子。

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"
}

0

如果我正在编写API,则会使用“返回此值”来设置只能设置一次的值。如果我还有用户可以更改的任何其他值,则改用标准的void设置器。

但是,这实际上是一个优先事项,我认为链设置器看起来确实很酷。


0

我同意所有发布者的观点,声称这违反了JavaBeans规范。有理由保留这一点,但我也觉得使用此Builder模式(已暗示)有其应有的地位。只要不是在所有地方都使用它,就应该可以接受。对我来说,“ It's Place”是端点对“ build()”方法的调用。

当然,还有其他方法可以设置所有这些内容,但是这样做的好处是它避免了1)多参数公共构造函数和2)部分指定的对象。在这里,让构建器收集所需的内容,然后在最后将其称为“ build()”,这可以确保未构建部分指定的对象,因为该操作的可见性不及公共。替代方案是“参数对象”,但是恕我直言,只是将问题推回了一个层次。

我不喜欢多参数构造函数,因为它们使传递许多相同类型的参数的可能性更大,这可以使将错误的参数传递给参数更容易。我不喜欢使用很多设置器,因为可以在完全配置对象之前使用该对象。此外,使用“ build()”方法可以更好地满足基于先前选择的默认值的概念。

简而言之,如果使用得当,我认为这是一个好习惯。


-4

坏习惯:二传手设置吸气剂

显式声明一个方法,该方法适用于U

setPropertyFromParams(array $hashParamList) { ... }

不利于自动完成,重构和可读性以及示例代码
Andreas Dietrich

因为:1)您必须记住命令或从文档中读取命令,2)松开所有编译时类型安全性,3)您必须强制转换值(this和2)在动态环境中当然不是问题当然不是这种语言),4)除了检查每次调用时的数组长度外,不能有效地扩展它; 5)如果您进行了任何更改(无论是顺序还是某些值的存在),您都无法检查两个运行时也不编译timewithout疑问可言。对于设置员:1),2),3):没问题。4)添加新方法不会破坏任何内容。5)明确的错误信息。
Adowrath
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.