未对 Int 执行隐式转换

Implicit conversion not performed on Int

在Scala中,我想为基本类型生成一些别名,然后通过一个类型实现转换class。这对我很有用,也是了解类型 classes 的机会。代码如下:

type Index = Int
val Index = Int

type Integer = Int
val Integer = Int

type Real = Double
val Real = Double // to have companion object of Double also be the companion object of Real

trait Convertible[A] {
  def toIndex(a: A): Index
  def toInteger(a: A): Integer
  def toReal(a: A): Real
}

implicit val ConvertibleIndex: Convertible[Index] = new Convertible[Index] {
  def toIndex(i: Index) = i
  def toInteger(i: Index) = i
  def toReal(i: Index) = i.toDouble
}

implicit val ConvertibleInteger: Convertible[Integer] = new Convertible[Integer] {
  def toIndex(i: Integer) = i
  def toInteger(i: Integer) = i
  def toReal(i: Integer) = i.toDouble
}

implicit val ConvertibleReal: Convertible[Real] = new Convertible[Real] {
  def toIndex(r: Real) = r.toInt
  def toInteger(r: Real) = r.toInt
  def toReal(r: Real) = r
}

implicit val ConvertibleString: Convertible[String] = new Convertible[String] {
  def toIndex(s: String) = s.toInt
  def toInteger(s: String) = s.toInt
  def toReal(s: String) = s.toDouble
}

implicit class ConvertibleSyntax[A](a: A)(implicit val c: Convertible[A]) {
  def toIndex = c.toIndex(a)
  def toInteger = c.toInteger(a)
  def toReal = c.toReal(a)
}

现在考虑:

val a = 3.toReal
val b = 3.0.toReal
val c = "3".toReal

a 的语句无法编译,编译错误:method toReal is not a member of Int。但是,对于 bc 语句,到 ConvertibleSyntax 的隐式转换是正确完成的。

为什么隐式转换不适用于 Int,但适用于 DoubleString

因为您为 IndexInteger(都是 Int)定义了模糊的隐式。

编译器应该选择哪一个?

我想您可能对 Scala 如何进行 隐式转换 感到有点困惑。 (一个常见的错误,因为 implicit 有点被过度使用了。)

我认为你首先想要的是一个隐式转换函数 - 或者甚至是一个隐式class.以下是使用后者的方法:

注意:IntIndexInteger 的处理方式相同,RealDouble 的处理方式相同,有些混淆,所以我已经将其简化为可行的东西。此外,Convertible 不需要是 generic 因为它的转换函数不需要参数。最后,您的类型不应同时具有 typeval 声明。

type Index = Int

type Integer = Int

type Real = Double

trait Convertible {
  def toIndex: Index
  def toInteger: Integer
  def toReal: Real
}

// Implicit classes cannot be defined in top-level scope, so they belong to an object.
object Implicits {

   implicit class ConvertibleInt(i: Int)
   extends Convertible {
     override def toIndex = i
     override def toInteger = i
     override def toReal = i.toDouble
   }

   implicit class ConvertibleDouble(d: Double)
   extends Convertible {
     override def toIndex = d.toInt
     override def toInteger = d.toInt
     override def toReal = d
  }

   implicit class ConvertibleString(s: String)
   extends Convertible {
     override def toIndex = s.toInt
     override def toInteger = s.toInt
     override def toReal = s.toDouble
  }
}

现在试试这个:

import Implicits._
val a = 3.toReal
val b = 3.0.toReal
val c = "3".toReal

这里发生了什么?好吧,implicit class 声明定义了 class 那些 修饰 具有附加函数的唯一构造函数参数。如果编译器发现您正在尝试在没有该方法的类型上调用方法,它将查看范围内是否存在 隐式转换 到类型。如果是,则使用它并调用该函数;如果不是,你会得到一个编译器错误。 (import 语句用于将 classes 带入当前范围。)

因此,例如,当编译器看到 "3".toReal 时,它首先确定 "3" 是一个 String。由于此类型没有 .toReal 成员,它会尝试找到从 String 到具有此类成员的类型的转换。它找到带有 String 参数并提供 .toReal 方法的 ConvertibleString 隐式 class。耶!因此,编译器通过将 "3" 传递给 ConvertibleString 的构造函数来创建此 class 的实例,然后在结果上调用 .toReal

另一方面,当 implicit 与一个值一起使用时,它告诉编译器该值是相同的任何匹配 隐式参数 的默认值未提供的类型。 切勿将 implicit 与原始或普通库类型一起使用!

例如:

final case class Example(i: Int)

// Default.
implicit val nameCanBeAnythingAtAll = Example(5)

// Function with implicit argument.
def someFunc(implicit x: Example): Unit = println(s"Value is $x")

现在,如果你这样写:

someFunc

输出将是 Value is Example(5)

implicit 值和参数是一个高级主题,我不会担心它们现在是如何使用的。