在Scala中,我想为基本类型生成一些别名,然后通过类型类实现转换。这对我有用,也是理解类型类的机会。代码如下:
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
。但是,对于b
和c
语句,隐式转换为ConvertibleSyntax
已正确完成。
为什么隐式转换不适用于Int
,但正在处理Double
和String
?
答案 0 :(得分:4)
因为您为Index
和Integer
定义了含糊不清的含义(Int
)。
编译器应该选择哪一个?
答案 1 :(得分:1)
我认为您可能会对 Scala 隐式转化的方式感到有些困惑。 (一个常见的错误,因为implicit
有点过度使用。)
我认为你想要的,首先是隐式转换函数 - 甚至是隐式类。以下是使用后者的方法:
注意:Int
,Index
和Integer
的处理方式相同,Real
和Double
也是如此,这有点令人困惑,所以我已经减掉了到一些可行的东西。此外,Convertible
不需要是泛型,因为其转换函数不需要参数。最后,您的类型不应同时包含type
和val
声明。
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
声明定义了装饰带有附加函数的唯一构造函数参数的类。如果编译器发现您正在尝试在没有该方法的类型上调用方法,那么它将查看是否存在范围内的隐式转换类型。如果是,则使用它并调用该函数;如果没有,则会出现编译错误。 (import
语句用于将类带入当前范围。)
因此,例如,当编译器看到"3".toReal
时,它首先确定"3"
是String
。由于此类型没有.toReal
成员,因此它会尝试查找从String
到具有此类成员的类型的转换。它找到ConvertibleString
隐式类,它接受String
参数并提供.toReal
方法。好极了!因此,编译器通过将"3"
传递给ConvertibleString
的构造函数来创建此类的实例,然后在结果上调用.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
值和参数是一个高级主题,我不会担心它们现在是如何使用的。