为什么在scala.language.implicitConversions不是最后一个导入时会发出警告?

8
在我的Scala代码中,我有一些隐式转换,并且已经引入了必要的导入:
import scala.language.implicitConversions

然而,有时候在这个导入之后,如果还有其他的导入,我会得到警告,就像这个导入不存在一样:

警告:(112, 18) 隐式转换方法pair2Dimension应该被启用, 通过让隐式值scala.language.implicitConversions可见。

build.sbt:

name := "ImplicitSBT"

version := "1.0"

scalaVersion := "2.11.5"

scalacOptions ++= Seq("-deprecation","-feature")

libraryDependencies += "org.scala-lang.modules" %% "scala-swing" % "1.0.1"

Main.scala:

import scala.language.implicitConversions
import scala.swing.{Action, _}

object Main extends App {

  implicit def pair2Dimension(pair: (Int, Int)): Dimension = new Dimension(pair._1, pair._2)

  val dim : Dimension = (0,0)

  println(dim)


}

为什么会发生这种情况?import scala.swing.{Action, _} 如何隐藏 implicitConversions 的导入呢?
3个回答

6

正如http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html所解释的那样,通配符导入(就像你写的那样)会给任何Swing隐式定义一个相当高的优先级,这显然会掩盖你的定义。

由于你正在使用SBT编译,传递以下设置不是更容易吗?

scalacOptions ++= Seq(
  "-feature",
  "-language:implicitConversions"
)

不要再担心导入 scala.language.implicitConversions 的正确位置了。


不知道为什么,我觉得使用import比编译器选项更加简洁。可能只是我这个人的感觉。 - Suma

5
“import scala.swing.{Action, _}” 是如何隐藏 “implicitConversions” 导入的?
import scala.language.implicitConversions

…被scala.swing包对象中定义的implicitConversions覆盖

package scala

...

package object swing {

...

implicit lazy val implicitConversions = scala.language.implicitConversions

...

}

由于您在此处使用通配符导入:

import scala.swing.{Action, _}

...scala.swing.implicitConversionsscala.swing中导入,并最终遮蔽scala.language.implicitConversions

有趣的问题是:为什么scalac无法确定语言特性是否启用,如果有两个“功能标志”(在这种情况下为implicitConversions),一个遮蔽另一个在相同级别。

这可能是一个错误,也可能是SIP 18实现的具体情况。

无论如何,为了解决这个问题,我建议你执行以下操作之一:

  1. 不要导入 scala.language.implicitConversions (因为在通配符导入 scala.swing 时已经导入)

  2. 不要从 scala.swing 进行通配符导入(不要污染你的作用域,仅导入需要的内容)

  3. 在你的 Main 对象级别执行另一个导入(不会被其他导入遮蔽)implicitConversions


需要澄清的是:有趣的问题不在于为什么这两个会发生冲突(这是预期的),而是当“scala.language”导入放在第二位时,为什么它们不会发生冲突。 - Travis Brown
@Travis 我认为一个特定(单独的)导入优先于其之前进行的通配符导入。 - Suma
@Suma 哦,对了,这很有道理。所以这并不是太神秘了。 - Travis Brown

1

这是隐式查找中的一个错误。

以下是更常规代码中相同的结构,其中所需的隐式是执行上下文。

(无论通配符导入是否来自包对象,或者其他包是否在同一编译单元中都没有关系。)

由于代码可以使用显式的 global 编译,因此应该可以使用隐式参数编译。

如果可以 不带前缀访问 隐式,则可以使用该隐式。

绑定优先级 不受源代码顺序的影响。遮蔽按照通常的方式工作;绑定永远不会遮蔽具有更高优先级的绑定。

/*
package object bound2 {
  implicit lazy val global = scala.concurrent.ExecutionContext.Implicits.global
}
*/
package bound2 {
  object B {
    implicit lazy val global: concurrent.ExecutionContextExecutor = scala.concurrent.ExecutionContext.global
  }
}

package bound {
  // the order of these imports in the same scope should not matter
  import scala.concurrent.ExecutionContext.Implicits.global
  import bound2.B._

  object Test extends App {
    val f = concurrent.Future(42)  //(global) // explicit arg works
    Console println concurrent.Await.result(f, concurrent.duration.Duration.Inf)
  }
}

在示例2.0.1中,添加标记为“OK”的行可以编译,并且您可以验证顺序无关紧要,但在内部作用域中变得不明确,因为那里的“通配符y”没有隐藏外部作用域中的“显式y”。
    import X.y           // `y' bound by explicit import
    println("L16: "+y)   // `y' refers to `Q.X.y' here
    import P.X._
    println("OK: "+y)   // `y' refers to `Q.X.y' here

    locally { val x = "abc"      // `x' bound by local definition
      import P.X._       // `x' and `y' bound by wildcard import
//        println("L19: "+y) // reference to `y' is ambiguous here

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接