【问题标题】:Method Chaining in Java [closed]Java中的方法链[关闭]
【发布时间】:2012-07-18 16:41:15
【问题描述】:

在之前在这里回答几个问题时以及最近我一直在做的一些工作中,我一直想知道为什么 Java 不支持其内置类的方法链接。

例如,如果我要创建一个 Car 类,我可以通过重新调用 this 而不是 void 来使其可链接,如下所示:

public class Car {
    private String make;        

    public Car setMake(String make) {
        this.make = make;
        return this;
    }   
}

内置库不倾向于以这种方式做事有什么特别的原因吗?方法链有缺点吗?

我可能忽略了一些可以解释缺少方法链接的东西,但是任何默认返回 void 的 setter 方法都应该返回对 this 的引用(至少在我看来应该如此)。这将使以下情况更加清晰。

container.add((new JLabel("label text")).setMaximumSize(new Dimension(100,200)));

而不是冗长的:注意:如果你愿意,它不会阻止你以这种方式编码。

JLabel label = new JLabel("label text");
label.setMaximumSize(new Dimension(100,200));
container.add(label);

我很想听听这个决定背后的原因,如果我不得不猜测这会是一个相关的开销,所以应该只在需要时使用。

【问题讨论】:

  • 您的示例也显示了方法链接的缺点:只需查看代码,您设置的最大大小并不是很明显:是标签还是容器?一个放错位置的右括号,事情可能会大错特错。
  • @JonTaylor 我不确定依靠空格/格式化是一个好的解决方案。
  • @ColinD 如果这只是一个可读性问题,那么我认为没有理由不使用空格来使其更清晰,这不是人们不写 if/else all on a single的原因吗行,或者确实将他们的整个程序写在一行上?
  • @GeorgeStocker 我不明白为什么应该关闭它,这是一个完全有效的措辞良好的问题,我已经得到了一些完全有效的答案。这不应该被关闭。
  • @JonTaylor 我同意,任何对问题和答案只看了一眼的人都可以看到它们包含的大部分事实信息也可能对其他人有用。

标签: java methods chaining method-chaining


【解决方案1】:

嗯。在两个方向都需要提出可读性论点——比如试图在一行中放太多东西。

但老实说,我怀疑这是出于历史原因:普遍的“连锁”行为并没有真正流行或广为人知。 Swing 正在开发中。您可能会争辩说它应该稍后添加,但这样的事情往往会造成二进制不兼容以及 Sun/Oracle 历来非常谨慎的其他问题。

更多最新的 JDK 库——参见例如ByteBuffer 举一个著名的主要例子——已经在有意义的地方提供了链接行为等。

【讨论】:

  • 是的,确实存在可读性问题。是的,他们不更新 swing 很遗憾,但 Swing 一直是我最不喜欢的 Java 方面之一。
  • 我相信这是正确的答案。作为历史记录,Java 的许多创始人都有 Smalltalk 的背景。 Smalltalk 没有很多“然后返回这个”方法,因为它有一个以分号形式链接方法的语法结构。也许这影响了 Java 的设计者。即使它们不包含链接运算符。
  • @JonTaylor “很遗憾他们不更新 swing”:他们确实这样做了,它被称为 Java Fx 2
  • 我要补充一点,调试是另一个重要原因。
  • ...这是投反对票的原因吗? (我不确定我是否买了这个作为一个原因——我自己调试流畅的语法并没有那么麻烦。在某些调试器中调试流畅的语法可能是一个缺陷,但我不会说那是语法本身的缺陷。)
【解决方案2】:

我能想到的另一个原因是性能,或者更准确地说:不要为不使用的东西付费。 return this 在每种方法之后的成本都不是很高,但仍然需要很少的额外 CPU 周期和一个 CPU 注册表。

甚至有想法在每个声明 void 返回值的方法中添加隐式 return this,但被拒绝了。

【讨论】:

  • JIT 真的不会优化它吗?至少,对于每个足够短的方法可以内联,它应该......
  • 请解释一下,哦,匿名投票者。
  • @LouisWasserman 是的,我指的是 甚至有一个想法,将隐式 return this 添加到每个声明 void 返回值的方法中 [...] 也许“句法”是错误的词,“编译时间”更好。我的意思是表达式<expr>.y()(其中y 是一个void 方法)的值将是<expr> 的值。换句话说,取消引用void 值将使编译器在取消引用链中回溯,直到找到非void 表达式。因此,这不会对性能产生任何影响(也许编译时除外),并且不会在字节码中可见。
  • TL;DR:编译器会让我们使用void 方法,就好像它返回了this,尽管它实际上不会。 (它不需要返回this,因为我们已经知道,在调用站点,它认为是this。)
  • 我不认为性能是完全避免这种方法的理由。如果您分析您的程序并发现链接是真正的瓶颈,那么请确定:不要使用它。但如果你还没有这样做,那么声称性能原因是过早的优化。
【解决方案3】:

虽然我们只能猜测真正的原因,但其中之一可能是严格来说它不是很OO。

如果您将方法视为代表建模世界的动作的动作,则方法链并不真正适合该画面。

另一个原因可能是当您尝试覆盖链式方法时可能会出现混乱。想象一下这种情况:

class Foo {
   Foo chainedMethod() {...}
}

class Bar extends Foo {
   Foo chainedMethod() {...} //had to return Foo for Java versions < 1.5
   void doStuff();
}

因此,当您尝试执行 new Bar().chainedMethod().doStuff() 时,它不会编译。而且((Bar)new Bar().chainedMethod()).doStuff() 看起来不太好,是吗:)

【讨论】:

  • 我不确定我是否同意,我会说你只是一次执行多个操作。
  • @JonTaylor 但是为什么在建模世界中一个方法会返回一个不会发生的值?
  • 我知道您来自哪里,但我认为这就像要求一个 启用了奶酪和番茄酱的汉堡,而不是一个 启用了奶酪和同一个汉堡的汉堡再次启用番茄酱。是的,我的例子很愚蠢,但我认为传达了我的观点(可能)。
  • @JonTaylor OO 解决方案是组合setCheeseAndKetchup() 操作。不得不说,我不一定同意这个观点,但是hardline OO是基于严格建模现实生活活动的方法原则。
  • 我也不是说我同意。我更喜欢实用的解决方案,每一个告诉你必须做什么和不应该做什么的理论都可以延伸到实际可用的范围之外。对 OO 的过于严格的解释可能是一种真正的痛苦,但是尝试使用方法链来做所有事情并且没有一点常识可能同样痛苦。
【解决方案4】:

方法链通常与 Builder 模式一起使用,您可以在 StringBuilder 类中看到这一点。除此之外,方法链接可以使命令和创建的分离不太清晰,例如repaint() 是否也应该是流利界面的一部分?那么我可以:

container.add(label).repaint().setMaximumSize(new Dimension(100,200)));

突然间,我的调用顺序变得很重要,这可能会隐藏在方法链中。

【讨论】:

  • 是的,我知道这种事情会让人感到困惑,但是仅仅因为有人提供了做某事的可能性并不意味着你总是必须这样做。对我来说,添加事件侦听器是使用链接的一个很好的例子,而上面突出显示的情况(如重绘调用)可能会变得令人困惑。但是你可以对三元运算符之类的东西说同样的话,如果在彼此内部使用,有些人会觉得它们很糟糕,而像我这样的其他人则更喜欢它们而不是标准的 if 语句(取决于场景)。
  • 我其实很喜欢用method chaining (aka fluent interface),虽然我知道不应该用得太远。
  • 是的,当然,我认为它可能非常有用,但确实意识到在某些情况下它可能会令人困惑,但正如我所说,仅仅因为有这样做的选项并不意味着人们拥有在各种情况下使用它。
【解决方案5】:

你真的在问Law of Demeter

“只与你的直系朋友交谈”

请注意,上面的链接是一个很棒的资源,但是您可能会在那里花费很长时间而得不到您想要的答案! :-)

【讨论】:

  • 除非我误解了你的意思,否则我认为我说的不是得墨忒耳法则。我不是在谈论对对象内的对象调用方法,只是在单个对象上链接方法调用。 得墨忒耳法则真的适用于此吗?
  • 视情况而定!我对您最初的问题的解释可能比您预期的更广泛,但它当然可以适用。
  • afaik,这里的主题是 一个对象,您需要在其上进行许多设置,并希望使用 o.settitle(x).setauthor( y).setdate(z) 而不是 o.settitle(x); o.setauthor(y); o.setdate(z);
  • -1 这与得墨忒耳法则无关(你总是在同一个对象实例上工作)。
  • @sleske 虽然我在这个问题的背景下同意你的观点,但我可以看到他来自哪里。原因是某些类仍然使用一种链接形式(看起来相同),但它们不一定返回您认为的对象。一大早我真的想不出任何例子,但虽然它们不是问题中提到的典型方法链接,但它们有点相似。
【解决方案6】:

未实现方法链接的原因可能有以下几点:

  • 方法链的最大问题是完成问题。 虽然有解决方法,但通常如果你遇到这种情况,你 最好使用嵌套函数。嵌套函数也更好 如果您陷入上下文变量的混乱,请选择。

  • 无法保证链外的对象实际上会返回一个有效的, 非空对象。此外,调试这种风格的代码通常很多 更难,因为许多 *IDE* 不会在调试时评估方法调用 时间作为您可以检查的对象

  • 当您将例程调用到其他类以 当有很多时,将参数传递给链接方法之一 要传递的参数主要是因为行很长

  • 还有一个性能问题,会在内存上吃力

【讨论】:

  • 匿名投反对票的人愿意解释他/她投反对票的原因吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-02
  • 2021-01-22
  • 2017-04-06
  • 2017-10-19
  • 1970-01-01
  • 2016-09-21
  • 2013-07-05
相关资源
最近更新 更多