【问题标题】:Passing function in ScalaScala中的传递函数
【发布时间】:2016-02-03 16:49:21
【问题描述】:

从 Java 8 开始我可以通过

xyz.foreach(e -> System.out.println(e));

我可以做到以下几点

xyz.foreach(System.out::println)

我看过this thread关于方法引用的工作原理,但问题如下:

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

对象 IanesJsonHelper 中的 toJson 方法(来源:IanesServer)String

对象IanesJsonHelper中的toJson方法(成功:Boolean)String

匹配预期类型 Function[?,String]

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

                                                        ^

我确实有 3 个名为“toJSON”的函数

def toJson(id: Int): String

 def toJson(success: Boolean): String

 def toJson(source: IanesServer): String

最后一个是正确的。

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

def toJson[T](source: Array[T], toJson: Function[T, String]): String

这是相关代码:

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

我不明白我的错误是什么:

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

有人能指出错误吗?目前我强烈反对该函数是 [?,String]。有什么想法吗?

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

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

【问题讨论】:

    标签: java scala generics java-8


    【解决方案1】:
    def toJson[T](source: Array[T], toJson: Function[T, String]): String
    

    您希望编译器推断 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)
      

    当您有两个参数列表时,传递给第一个列表的参数将告诉编译器如何绑定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)
    

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

    Scala 与 Java 或 C# 不同,它使用函数参数列表中的所有参数来计算它们的 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)
    

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

    另一方面,C#,wouldn't allow this

    编译错误(第 17 行,第 3 列):无法从用法推断方法“Program.F(T,T)”的类型参数。尝试明确指定类型参数。

    最后一点:LUBing 很棒,但也展示了它的disadvantages

    【讨论】:

    • 这很糟糕,Scala 编译器没有那么聪明。足够的。问题是,(2)不起作用(IntelliJ 抱怨说,没有任何方法具有这种特征)。我现在正在使用“IanesJsonHelper.toJson[IanesServer](array,IanesJsonHelper.toJson)”。非常感谢! PS:感谢您多次编辑您的帖子 :) 确实有助于使事情更清晰(老实说,没有讽刺)
    • @Reisi007 我知道,对吧?这对我来说也很令人惊讶,尤其是来自可以编译的 C# 背景。尽管有这个小怪癖,我仍然会选择 scala 的类型推断而不是 C# 的任何一天。
    • 我来自Java,完全没有问题..(我猜,未经测试)。我刚刚习惯了 Scala。无论如何,感谢您的快速回答。非常感谢:D
    • @Reisi007 经过进一步思考,我意识到为什么编译器会出现这种行为是有原因的。我已经更新了我的帖子,希望对您有所帮助:)
    猜你喜欢
    • 2015-02-10
    • 1970-01-01
    • 2019-06-19
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    相关资源
    最近更新 更多