在Scala中传递函数

Flo*_*ger 4 java generics scala java-8

从Java 8开始我就可以通过了

xyz.foreach(e -> System.out.println(e));
Run Code Online (Sandbox Code Playgroud)

我可以做以下事情

xyz.foreach(System.out::println)
Run Code Online (Sandbox Code Playgroud)

我看过这个关于方法引用如何工作的线程,但问题如下:

错误:(16,63)对重载定义的模糊引用,

两个方法toJson在对象IanesJsonHelper中的类型(source:IanesServer)String

和方法toJson在对象IanesJsonHelper中的类型(success:Boolean)String

匹配期望类型函数[?,String]

 val json = IanesJsonHelper.toJson(array,IanesJsonHelper.toJson _)

                                                        ^
Run Code Online (Sandbox Code Playgroud)

我有3个名为"toJSON"的函数

def toJson(id: Int): String
Run Code Online (Sandbox Code Playgroud)

 def toJson(success: Boolean): String
Run Code Online (Sandbox Code Playgroud)

 def toJson(source: IanesServer): String
Run Code Online (Sandbox Code Playgroud)

最后一个是正确的.

我在上面的错误消息中调用的函数是:

def toJson[T](source: Array[T], toJson: Function[T, String]): String
Run Code Online (Sandbox Code Playgroud)

这是相关代码:

 val array = new Array[IanesServer](1)
 array(0) = new IanesServer(1, "http://localhost:4567/", "Test")
 val json = IanesJsonHelper.toJson(array,IanesJsonHelper.toJson)
Run Code Online (Sandbox Code Playgroud)

我不知道我的错误是什么:

  1. 该数组的类型为IanesServer
  2. 调用方法中的T应该是IanesServer(Array [IanesServer] - > Array [T]
  3. 由于2.函数中的T必须与数组中的T相同,因此必须是函数[IanesServer,String] - > Function [T,String]

有人可以帮助指出错误吗?目前我非常不同意,该函数是[?,String].有任何想法吗?

答:谢谢您的快速回答,这是我选择的:

 IanesJsonHelper.toJson[IanesServer](array,IanesJsonHelper.toJson)
Run Code Online (Sandbox Code Playgroud)

dca*_*tro 5

def toJson[T](source: Array[T], toJson: Function[T, String]): String
Run Code Online (Sandbox Code Playgroud)

您希望编译器推断toJson为类型,Function[IanesServer, String]因为source它是类型Array[IanerServer]- 因此是T等于IanesServer.

不幸的是,scala编译器并不那么聪明.有两种方法可以帮助编译器:

  1. 明确说明类型

    IanesJsonHelper.toJson[IanesServer](array, IanesJsonHelper.toJson _)

  2. 将参数拆分为两个参数列表:

    def toJson[T](source: Array[T])(toJson: Function[T, String]): String
    
    IanesJsonHelper.toJson(array)(IanesJsonHelper.toJson)
    
    Run Code Online (Sandbox Code Playgroud)

当您有两个参数列表时,传递给第一个列表的参数将告诉编译器如何绑定T,编译器将使用这些绑定用于其余列表.

这是另一个较短的例子:

// doesn't compile - the compiler doesn't realize `_` is an Int and therefore doesn't know about the `+` operator
def map[A, B](xs: List[A], f: A => B) = ???
map(List(1,2,3), _ + 1)  

//compiles fine
def map[A, B](xs: List[A])(f: A => B) = ???
map(List(1,2,3))(_ + 1)
Run Code Online (Sandbox Code Playgroud)

这种行为可能看起来很不幸,但这是有原因的.

与Java或C#不同,Scala使用函数参数列表中的所有参数来计算其LUB(最小上限),并使用它来推断函数的泛型类型参数.

例如:

scala> def f[A](x: A, y: A): A = x
f: [A](x: A, y: A)A

scala> f(Some(1), None)
res0: Option[Int] = Some(1)
Run Code Online (Sandbox Code Playgroud)

这里,scala使用两个参数(类型Some[Int]None)来推断A(Option[Int]- LUB)的类型.这就是为什么scala需要你告诉它toJson你指的是哪个超载.

另一方面,C#不允许这样做.

编译错误(第17行,第3栏):无法从用法推断出方法'Program.F(T,T)'的类型参数.尝试显式指定类型参数.

最后一个注意事项:LUBing很棒,但也有它的缺点.