我试图理解 scala 隐式及其在类型类中的用法。 我有一个通用特征 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");
}
}
但是,当我尝试使用泛型类中的相同内容时,如下所示,会导致错误无法找到参数 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");
}
}
隐式是在对象中定义的,我猜测它们也在范围中可用。
最佳答案
具体类型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")
}
}
关于Scala泛型类型和隐式类型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50817600/