How can I programmatically make methods chainable?

前端 未结 4 1517
栀梦
栀梦 2021-02-09 05:30

Let\'s say I have a class and I want to make its methods chainable, I could do something like this:

class MyClass {

  def methodOne(arg1: Any): MyClass = {
             


        
4条回答
  •  孤独总比滥情好
    2021-02-09 06:07

    The first option is the most efficient one, the other one introduces overhead by wrapping code into function object. But it's certainly possible to create such a wrapper. Let's define

    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
    }
    

    Notice this.type, it says the result of our functions is the type of the class they're defined in. So now when we mix it into our class

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

    the result of methodTwo will be MyClass.


    Update: There is another option, to use implicit conversions:

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

    Calling chain converts anything to this.type. However it only works inside the class, you can't call something like new MyClass2.methodOne(3).chain.methodTwo("x") outside.


    Update: Yet another solution, based on implicit conversion from Unit to 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);
    }
    

提交回复
热议问题