'Spread' parameters in Scala?

后端 未结 2 2093
甜味超标
甜味超标 2020-12-11 03:01

Is there any way to call a Scala function that takes individual parameters, given an array (similar to JavaScript Spreads in ECMAScript 6)?



        
2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-11 03:52

    I think you would have to throw type safety away and resort to using reflection.

    scala> object Foo {
     | def doFoo(i:Int, s:String) = println(s * i)
     | }
    
    defined module Foo
    
    scala> val fooFunc = Foo.doFoo _
    fooFunc: (Int, String) => Unit = 
    
    scala> val applyMeth = fooFunc.getClass.getMethods()(0)
    applyMeth: java.lang.reflect.Method = public final void $anonfun$1.apply(int,java.lang.String)
    
    scala> val i:Integer = 5
    
    i: Integer = 5
    
    scala> val seq = Seq[Object](i,"do the foo! ")
    seq: Seq[Object] = List(5, "do the foo! ")
    
    scala> applyMeth.invoke(fooFunc, seq :_*)
    do the foo! do the foo! do the foo! do the foo! do the foo! 
    res59: Object = null
    

    However, unless you are creating some DSL and realy need this kind of feature, I'd try to find another way. Either overload the methods if it's under your control or wrapp it in some facade kind of class.

    EDIT

    To answere Rubistro's questions in the comments.

    a) How would this technique work to call foo.doFoo? (That is, you have no object -- just an instance of a class that defines doFoo.)

    val fooFunc is an instance of a Function2 it is that instance apply function that gets called when invoking applyMeth.invoke(fooFunc, seq :_*).

    b) does this method allow parameters to be passed to doFoo before and after the values in seq?

    Not directly. To use this you would have to build your sequence before invoking the method. However, since it's a Sequence you could easily prepend/append values to the sequence you are using before invoking the method. Wrapping it up in a builder might be usefull e.g.

    class InvokationBuilder(meth:java.lang.reflect.Method, args: List[Object] = Nil) {
        def invoke(instance:Object) = meth.invoke(instance, args.toSeq :_*)
        def append(arg:Object) = new InvokationBuilder(meth, args :+ arg)
        def prepend(arg:Object)= new InvokationBuilder(meth, arg :: args)
    }
    

提交回复
热议问题