我试图了解scala implicits及其在类型类中的用法。 我有一个通用特征FromString和一个伴随对象,用于为标准类型定义FromString的隐式实例,如下所示:
file: /src/main/scala/util/FromString.scala
package util
trait FromString[A] {
def fromString(string: String): A
}
object FromString {
def toFromString[T](p: String => T): FromString[T] = new FromString[T] {
def fromString(x: String): T = p(x);
}
implicit val IntFromString = toFromString[Int](_.toInt);
implicit val ByteFromString = toFromString[Byte](_.toByte);
implicit val LongFromString = toFromString[Long](_.toLong);
implicit val ShortFromString = toFromString[Short](_.toShort);
implicit val FloatFromString = toFromString[Float](_.toFloat);
implicit val DoubleFromString = toFromString[Double](_.toDouble);
implicit val BooleanFromString = toFromString[Boolean](_.toBoolean);
implicit val IntListFromString = toFromString[List[Int]](_.split(',').map(_.toInt).toList);
def convertFromString[A](string: String)(implicit e: FromString[A]): A = e.fromString(string)
}
现在我可以使用FromString对象的convertFromString函数将字符串转换为标准类型,如下所示。这运行正确。
file: /src/main/scala/top/Main1.scala
package top
import util.FromString._
object Main {
def main(args: Array[String]): Unit = {
val d = convertFromString[Double]("4.5");
val i = convertFromString[Int]("42");
val li = convertFromString[List[Int]]("1,2,3");
println(s"d=$d i=$i li=$li");
}
}
但是,当我尝试在泛型类中使用相同的东西时,如下所示,它会导致错误could not find implicit value for parameter e: util.FromString[T]
file: /src/main/scala/util/Knob.scala
package util
import FromString._
class Knob[T](val name: String, default: T){
var value: T = default;
def update(valstr: String) {
value = convertFromString[T](valstr);
}
}
file: /src/main/scala/top/Main2.scala
package top
import util.Knob
import util.FromString._
object Main {
def main(args: Array[String]): Unit = {
val width = new Knob[Int]("Width", 3);
width.update("100");
println(s"width=$width");
}
}
implicits在对象中定义,我猜它们也在范围内可用。
答案 0 :(得分:1)
具体类型Int
仅在行
val width = new Knob[Int]("Width", 3);
在此行之前,编译器不知道类型T
将是什么,并且它无法在任何地方找到并插入IntFromString
作为隐式参数。因此,您必须将IntFromString
从main
中的调用网站传递到Knob.update
中的使用网站。为此,只需将FromString
类型类添加到T
:
class Knob[T: FromString](val name: String, default: T) {
...
}
使用泛型类型和类型类编程时的一般规则:必须在“世界末尾”插入具体的类型类实例,在泛型类型参数(如T
)的行中修复并变为具体类型(如Int
)。您必须将这些类型类实例从可以确定具体类型的站点一直传递到使用类型类实例的通用代码。
trait FromString[A] {
def fromString(string: String): A
}
object FromString {
def toFromString[T](p: String => T): FromString[T] = new FromString[T] {
def fromString(x: String): T = p(x);
}
implicit val IntFromString = toFromString[Int](_.toInt);
implicit val ByteFromString = toFromString[Byte](_.toByte);
implicit val LongFromString = toFromString[Long](_.toLong);
implicit val ShortFromString = toFromString[Short](_.toShort);
implicit val FloatFromString = toFromString[Float](_.toFloat);
implicit val DoubleFromString = toFromString[Double](_.toDouble);
implicit val BooleanFromString = toFromString[Boolean](_.toBoolean);
implicit val IntListFromString = toFromString[List[Int]](_.split(',').map(_.toInt).toList);
def convertFromString[A](string: String)(implicit e: FromString[A]): A = e.fromString(string)
}
import FromString._
class Knob[T: FromString](val name: String, default: T) {
var value: T = default
def update(valstr: String) {
value = convertFromString[T](valstr)
}
}
object Main {
def main(args: Array[String]): Unit = {
val d = convertFromString[Double]("4.5");
val i = convertFromString[Int]("42");
val li = convertFromString[List[Int]]("1,2,3");
println(s"d=$d i=$i li=$li");
val width = new Knob[Int]("Width", 3)
width.update("100")
println(s"width=$width")
}
}