【问题标题】:Scala 'type mismatch error' while using a function but works fine for methodScala在使用函数时出现“类型不匹配错误”,但适用于方法
【发布时间】:2018-06-13 20:32:51
【问题描述】:
def stringToIntMethod(input:String):Option[Int] = {
  try{
    Some(Integer.parseInt(input.trim()))
  }
  catch{
    case e:Exception => None
  }
}

val stringToIntFunction: (String) => Option[Int] = (in:String) => {
  try{
    Some(Integer.parseInt(in.trim()))
  }
  catch{
    case e:Exception => None
  }
}

val stringAndIntArray = Array("Hello", "1","2","Hi") //Input

println("with Method is: " + stringAndIntArray.flatMap(stringToIntMethod))
println("with functon is: " + stringAndIntArray.flatMap(stringToIntFunction))

在 flatMap 中使用 stringToIntFunction 时出现类型不匹配错误

type mismatch;
  found   : String => Option[Int]
  required: String => scala.collection.GenTraversableOnce[?]
println("with functon is: " + stringAndIntArray.flatMap(stringToIntFunction))
                                                    ^

为什么会这样?

【问题讨论】:

    标签: scala


    【解决方案1】:

    flatMap 需要 lambda,您传递的是普通方法

    这里是修复

    stringAndIntArray.flatMap(stringToIntMethod _)
    

    Scala REPL

    scala> def toInt(s: String): Option[Int] = Some(s.toInt)
    toInt: (s: String)Option[Int]
    
    scala> Array("1", "2", "3").flatMap(toInt _)
    res1: Array[Int] = Array(1, 2, 3)
    

    普通方法可以使用下划线转换为 lambda

    为清楚起见提供更多示例

    scala> def foo(s: String, i: Int): Double = 1
    foo: (s: String, i: Int)Double
    
    scala> foo _
    res2: (String, Int) => Double = $$Lambda$1162/1477996447@62faf77
    
    scala> foo(_, _)
    res3: (String, Int) => Double = $$Lambda$1168/1373527802@30228de7
    
    
    scala> foo(_: String, _: Int)
    res5: (String, Int) => Double = $$Lambda$1183/477662472@2adc1e84
    
    
    scala> foo("cow", _: Int)
    res7: Int => Double = $$Lambda$1186/612641678@146add7b
    
    scala> foo("Cow is holy", _: Int)
    res8: Int => Double = $$Lambda$1187/1339440195@7d483ebe
    

    还添加了来自 lambda 的注释。 xy。 x

    f _ is syntactic sugar for f(_) which is again syntactic sugar for x => f(x)
    

    【讨论】:

    • 也许是对下划线的一个小细节:f _f(_) 的语法糖,又是x => f(x) 的语法糖。
    • @lambda.xy.x 感谢您的意见!
    • 也称为“eta 扩展”stackoverflow.com/questions/39445018/…
    • 我想使用 stringToIntFunction,而不是 stringToIntMethod,即使没有 _ 或 lambda 对话也可以正常工作。
    • @Omprakash 是的,因为它是一个 lambda(函数)
    【解决方案2】:

    我仍然对为什么这个例子不起作用感到困惑。我认为它与 Scala 的类型推断有关。如果你看错误信息

     found   : String => Option[Int]
     required: String => scala.collection.GenTraversableOnce[?]
    

    它表示 stringToIntFunction 的返回值与 flatMap 的参数值不匹配。确实,型式测试

    Some(1).isInstanceOf[TraversableOnce[Int]]
    

    导致:

    <console>:138: warning: fruitless type test: a value of type Some[Int] cannot also be a scala.collection.TraversableOnce[Int] (the underlying of TraversableOnce[Int])
      Some(1).isInstanceOf[TraversableOnce[Int]]
                          ^
    res24: Boolean = false
    

    有趣的是,当我将函数的返回类型更改为TraversableOnce[Int] 时,它可以工作:

    def stringToIntFunction : String => TraversableOnce[Int] = (in:String) => {
       //...
    }
    

    导致

    scala> stringAndIntArray.flatMap(stringToIntFunction)
    res28: Array[Int] = Array(1, 2)
    

    原因是即使Option 不是从TraversableOnce 派生的,也存在隐式转换:

     scala> def f(to : TraversableOnce[Int]) = to.size
     f: (to: TraversableOnce[Int])Int
     scala> f(Some(1))
     res25: Int = 1
    

    之前在不同的question 中也注意到了这一点。

    我的理论是,对于一个方法,编译器明确知道返回值,这允许它检测到来自Option[Int] =&gt; TraversableOnce[Int] 的隐式转换的存在。但在函数值的情况下,编译器只会寻找(String =&gt; Option[Int]) =&gt; (String =&gt; TraversableOnce[Int]) 之间的隐式转换。当传递 lambda 应用程序 stringToIntFunction _ 时,编译器似乎看到它可以再次应用隐式转换。

    【讨论】:

    • 将返回类型更改为 Traversable 并不是一个好主意,因为它包含隐式转换。那么最好直接返回List(Integer.parseInt(in.trim()))
    • 是的,有道理。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 1970-01-01
    相关资源
    最近更新 更多