如何以编程方式使方法可链接?

jha*_*ing 7 scala

假设我有一个类,我想让它的方法可链接,我可以这样做:

class MyClass {

  def methodOne(arg1: Any): MyClass = {
    println("doing stuff")
    this
  }

  def methodTwo(arg1: Any, arg2: Any): MyClass = {
    println("doing other stuff")
    this
  }
}
Run Code Online (Sandbox Code Playgroud)

虽然这样可以实现我正在寻找的功能,但在我看来并不是很优雅.有没有更好的方法呢?

假设它是可能的,我希望能够做类似以下的事情,但我不确定如何处理该makeChainable功能.

class MyClass {

  val methodOne: Any => MyClass = 
    makeChainable((arg1: Any) => println("doing stuff"))

  val methodTwo: (Any, Any) => MyClass = 
    makeChainable((arg1: Any, arg2: Any) => println("doing other stuff"))

}
Run Code Online (Sandbox Code Playgroud)

Pet*_*lák 4

第一个选项是最有效的,另一个选项通过将代码包装到函数对象中而引入开销。但创建这样的包装器当然是可能的。让我们定义一下

trait Chainable {
  final def mkChain(f: () => Any): () => this.type =
    () => { f(); this; }
  final def mkChain[A](f: (A) => Any): (A) => this.type =
    (x: A) => { f(x); this; }
  final def mkChain[A,B](f: (A,B) => Any): (A,B) => this.type =
    (x: A, y: B) => { f(x, y); this; }
  // etc. for other arities
}
Run Code Online (Sandbox Code Playgroud)

注意this.type,它说我们函数的结果是它们定义的类的类型。所以现在当我们将它混合到我们的类中时

class MyClass extends Chainable {
  val methodTwo =
    mkChain((x: Any, y: String) => println("Doing something " + y));
}
Run Code Online (Sandbox Code Playgroud)

的结果methodTwo将是MyClass.


更新:还有另一种选择,使用隐式转换:

trait ToChain {
  implicit class AsThis(val _underlying: Any) {
    def chain: ToChain.this.type = ToChain.this
  }
}

class MyClass2 extends ToChain {
  def methodOne(arg1: Any): Unit =
    println("Doing something")
  def methodTwo(arg1: String): Unit =
    println("Doing something else " + arg1)

  methodOne(3).chain.methodTwo("x");
}
Run Code Online (Sandbox Code Playgroud)

调用chain将任何内容转换为this.type. 然而它只在类内部有效,你不能在new MyClass2.methodOne(3).chain.methodTwo("x")外部调用类似的东西。


更新:另一个解决方案,基于从Unit到 的隐式转换this

import scala.language.implicitConversions

class Chain[A](val x: A) {
  implicit def unitToThis(unit: Unit): A = x;
}
implicit def unchain[A](c: Chain[A]): A = c.x;

// Usage:

val r: MyClass = new Chain(new MyClass) {
  x.methodOne(1).methodTwo(2,3);
}
Run Code Online (Sandbox Code Playgroud)