当方法试图访问不存在的Case类成员时,Scala隐式方法编译。

问题描述 投票:0回答:1

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

我使用的是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不是case类的成员。

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 */ }
}
scala implicit scala-compiler
1个回答
4
投票

编译器为了解决一个缺失的方法val,做了很多事情。a.foobar.

它将检查这个方法是否属于 case class A它将检查A是否可以隐式转换为包含方法的不同类型 foobar 或者如果有 implicit class 增加了方法 foobar.

最终,它决定这个方法不可用,因此你看到了编译器错误。

如果你使用 a.x的隐式转换,编译器确实发现了一个从 AB 提供方法值的 x. 不幸的是,它并没有捕捉到这种情况在实际转换中发生的事实。在这种情况下,编译器所做的是按照以下步骤进行的

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

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

© www.soinside.com 2019 - 2024. All rights reserved.