方法尝试访问不存在的案例类成员时的 Scala 隐式方法编译

标签 scala implicit scala-compiler

我确实遇到了隐式方法的 Scala 编译器问题。场景很简单。隐式方法的任务是将案例类 A 的对象转换为案例类 B 的对象。隐式方法实现访问 A 的一个不存在的案例类成员。如果案例类成员在案例类 A 或 B 中根本不存在(例如 foobar),编译器会抛出错误。如果案例类成员确实存在于案例类 B 中,编译器不会抛出错误,即使我使用此名称(即成员 x)访问案例类 A。

我使用的是 2.13.1 版的 Scala。目前,2.13.2 是最新版本。

以下代码更详细地显示了该场景。以下代码将导致编译错误。

package Hokuspokus

object ImplicitMagic extends App {

  case class A(a: String, b: String, c: String)
  case class B(d: String, e: String, f: String, x: String)

  implicit def AtoB: A => B = a => B(a.a, a.b, a.c, a.foobar)

  def print(b: B): Unit = {
    System.out.println("Print" + b.d)
  }

  val a = A("foo", "bar", "asdf")

  print(a)

}

编译器指出以下错误:

[ERROR]   implicit def AtoB: A => B = a => B(a.a, a.b, a.c, a.foobar)
[ERROR]                                                       ^
[ERROR] one error found

但是,即使 x 不是案例类的成员,以下代码也不会遇到编译器错误:

package Hokuspokus

object ImplicitMagic extends App {

  case class A(a: String, b: String, c: String)
  case class B(d: String, e: String, f: String, x: String)

  implicit def AtoB: A => B = a => B(a.a, a.b, a.c, a.x)

  def print(b: B): Unit = {
    System.out.println("Print" + b.d)
  }

  val a = A("foo", "bar", "asdf")

  print(a)

}

我现在想知道,为什么 Scala 编译器在编译时没有检测到这个问题。为了了解 scalac 编译器的作用,我调查了已编译的 scala 类,但到目前为止我还没有得出结论。

package Hokuspokus
object ImplicitMagic extends scala.AnyRef with scala.App {
  def this() = { /* compiled code */ }
  case class A(a: scala.Predef.String, b: scala.Predef.String, c: scala.Predef.String) extends scala.AnyRef with scala.Product with scala.Serializable {
    val a: scala.Predef.String = { /* compiled code */ }
    val b: scala.Predef.String = { /* compiled code */ }
    val c: scala.Predef.String = { /* compiled code */ }
    def copy(a: scala.Predef.String, b: scala.Predef.String, c: scala.Predef.String): Hokuspokus.ImplicitMagic.A = { /* compiled code */ }
    override def productPrefix: java.lang.String = { /* compiled code */ }
    def productArity: scala.Int = { /* compiled code */ }
    def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
    override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
    def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
    override def productElementName(x$1: scala.Int): java.lang.String = { /* compiled code */ }
    override def hashCode(): scala.Int = { /* compiled code */ }
    override def toString(): java.lang.String = { /* compiled code */ }
    override def equals(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
  }
  object A extends scala.runtime.AbstractFunction3[scala.Predef.String, scala.Predef.String, scala.Predef.String, Hokuspokus.ImplicitMagic.A] with java.io.Serializable {
    def this() = { /* compiled code */ }
    final override def toString(): java.lang.String = { /* compiled code */ }
    def apply(a: scala.Predef.String, b: scala.Predef.String, c: scala.Predef.String): Hokuspokus.ImplicitMagic.A = { /* compiled code */ }
    def unapply(x$0: Hokuspokus.ImplicitMagic.A): scala.Option[scala.Tuple3[scala.Predef.String, scala.Predef.String, scala.Predef.String]] = { /* compiled code */ }
  }
  case class B(d: scala.Predef.String, e: scala.Predef.String, f: scala.Predef.String, x: scala.Predef.String) extends scala.AnyRef with scala.Product with scala.Serializable {
    val d: scala.Predef.String = { /* compiled code */ }
    val e: scala.Predef.String = { /* compiled code */ }
    val f: scala.Predef.String = { /* compiled code */ }
    val x: scala.Predef.String = { /* compiled code */ }
    def copy(d: scala.Predef.String, e: scala.Predef.String, f: scala.Predef.String, x: scala.Predef.String): Hokuspokus.ImplicitMagic.B = { /* compiled code */ }
    override def productPrefix: java.lang.String = { /* compiled code */ }
    def productArity: scala.Int = { /* compiled code */ }
    def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
    override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
    def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
    override def productElementName(x$1: scala.Int): java.lang.String = { /* compiled code */ }
    override def hashCode(): scala.Int = { /* compiled code */ }
    override def toString(): java.lang.String = { /* compiled code */ }
    override def equals(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
  }
  object B extends scala.runtime.AbstractFunction4[scala.Predef.String, scala.Predef.String, scala.Predef.String, scala.Predef.String, Hokuspokus.ImplicitMagic.B] with java.io.Serializable {
    def this() = { /* compiled code */ }
    final override def toString(): java.lang.String = { /* compiled code */ }
    def apply(d: scala.Predef.String, e: scala.Predef.String, f: scala.Predef.String, x: scala.Predef.String): Hokuspokus.ImplicitMagic.B = { /* compiled code */ }
    def unapply(x$0: Hokuspokus.ImplicitMagic.B): scala.Option[scala.Tuple4[scala.Predef.String, scala.Predef.String, scala.Predef.String, scala.Predef.String]] = { /* compiled code */ }
  }
  implicit def AtoB: scala.Function1[Hokuspokus.ImplicitMagic.A, Hokuspokus.ImplicitMagic.B] = { /* compiled code */ }
  def print(b: Hokuspokus.ImplicitMagic.B): scala.Unit = { /* compiled code */ }
  val a: Hokuspokus.ImplicitMagic.A = { /* compiled code */ }
}

最佳答案

编译器正在执行多项操作来解决缺少的方法/val a.foobar

它将检查此方法是否属于case class A,它将检查 A 是否可以隐式转换为包含方法 foobar 的不同类型,或者是否存在添加方法 foobar隐式类

最终,它决定此方法不可用,因此您会看到编译器错误。

如果您使用 a.x,编译器会找到从 AB 的隐式转换,它提供方法/val x 。不幸的是,它没有捕捉到这种情况发生在实际转换中的事实。编译器在这种情况下正在做什么

implicit def AtoB: A => B = a => B(a.a, a.b, a.c, AtoB(a).x)

这会编译,但会在运行时产生 StackOveflowException。

关于方法尝试访问不存在的案例类成员时的 Scala 隐式方法编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62194645/

相关文章:

Scala - 如何在运行时从外部文件编译代码?

scala - 如何以编程方式调用 Scala 编译器?

scala - Play/Scala 将 Controller 注入(inject)测试

scala - 通配符导入,然后隐藏特定隐式?

scala - 使用绑定(bind)在类类型参数中的上下文

scala - 尽管优先考虑了隐式,为什么我会收到 "ambiguous implicits"错误?

scala - 使用惰性参数初始化类时 Scala 编译器的奇怪行为

Scala 隐式类型转换和 ==

scala - 解释一个 Scala 类和对象?

scala - Scala 中的 forSome 关键字有什么用?