【问题标题】:Scala - how to pattern match when chaining two implicit conversions?Scala - 链接两个隐式转换时如何进行模式匹配?
【发布时间】:2019-02-24 21:33:19
【问题描述】:

我编写了一个解析 Metrics 数据的方法,最初遇到了一个问题,即 transactionMap 的类型是 java.util.Map。我使用 JavaConverters 解决了它。

def parseMetrics(metric: Metric) = {
    import scala.collection.JavaConverters._
    metric.transactionMap.asScala.values.map {
      case false => "N" 
      case true => "Y"
    }.toList

但在那之后我在模式匹配真假值时出错:pattern type is incompatible with expected type, found: Boolean, required: java.lang.Boolean

据我了解,Scala 不会链接两个隐式转换。有没有办法使用 JavaConverters 修复它?

【问题讨论】:

  • 你能把匹配语句改成x => if (x) "Y" else "N"吗?
  • @AndyHayden 成功了,谢谢!你能告诉我为什么这有助于解决问题吗?我猜这是因为我们没有隐式声明真假值,但不太确定

标签: java scala collections pattern-matching


【解决方案1】:

另一个答案提供了解决此问题的合理方法,但没有说明您遇到它的原因或它提出的方法如何工作。

Scala 标准库确实提供了从 java.lang.Booleanscala.Boolean 的隐式转换,您可以通过在 REPL 中使用 reify 来对一些在 Scala 上下文中使用 Java 布尔值的代码进行脱糖处理应为布尔值:

scala> val x: java.lang.Boolean = true
x: Boolean = true

scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify

scala> reify(if (x) 1 else 0)
res0: reflect.runtime.universe.Expr[Int] =
Expr[Int](if (Predef.Boolean2boolean($read.x))
  1
else
  0)

问题在于,仅仅尝试将java.lang.Boolean 值与truefalse 进行匹配不足以触发转换。你可以通过定义你自己的类型来检查这一点,你可以确定你确切地知道什么转换在起作用:

scala> case class Foo(i: Int); case class Bar(i: Int)
defined class Foo
defined class Bar

scala> implicit def foo2bar(foo: Foo): Bar = Bar(foo.i)
foo2bar: (foo: Foo)Bar

scala> Foo(100) match { case Bar(x) => x }
<console>:17: error: constructor cannot be instantiated to expected type;
 found   : Bar
 required: Foo
       Foo(100) match { case Bar(x) => x }
                             ^

这是一个语言设计决定。在这些场景中应用隐式转换可能是可能的,但也可能有一个很好的理由,它们不是(我不知道我不熟悉任何相关的讨论或问题,但这并不不是说它们不存在)。

Andy 的解决方案有效的原因是java.lang.Boolean 处于编译器期望scala.Boolean(一个条件)并且愿意应用Predef.Boolean2boolean 转换的位置。如果您真的想这样做,您可以手动执行此操作:

def parseMetrics(metric: Metric) = {
  import scala.collection.JavaConverters._
  metric.transactionMap.asScala.values.map(Predef.Boolean2boolean).map {
    case false => "N" 
    case true => "Y"
  }.toList
}

...但在我看来,至少 Boolean 上的模式匹配比使用条件要笨拙一些。

【讨论】:

    【解决方案2】:

    使用 if/else 而不是 match 语句进行布尔检查:

    def parseMetrics(metric: Metric) = {
        import scala.collection.JavaConverters._
        metric.transactionMap.asScala.values.map {
          x => if (x) "Y" else "N"
        }.toList
    

    我的怀疑是在 if 语句中 java.lang.Boolean (我假设 x 在这里)可以通过 import scala.collection.JavaConverters._ 强制转换为 Boolean... 但 match 语句不会做同样的强制,但必须显式进行(或匹配 java.lang.Boolean 值)。

    【讨论】:

    • JavaConverters 不需要将java.lang.Boolean 转换为scala.Boolean
    • 这是一个合理的解决方法,但它并不能真正回答问题,而且您的怀疑是不正确的。 :)
    • @TravisBrown 我在写这篇文章时在文档中读到 if 语句必须是 scala Boolean (但当然我再也找不到参考了),这向我表明需要强制... 有趣。
    猜你喜欢
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    • 2018-08-06
    • 2019-09-04
    • 1970-01-01
    • 1970-01-01
    • 2021-04-08
    • 2013-12-15
    相关资源
    最近更新 更多