scala 选项的隐式转换

4urapxun  于 2023-10-18  发布在  Scala
关注(0)|答案(2)|浏览(156)

我想表达如下:

  1. val input: Option[String] = Option("123")
  2. val longOption1: Option[Long] = input
  3. println(longOption1)
  4. // Some(123)
  5. val longOption2: Option[Long] = Option("abc")
  6. println(longOption2)
  7. // None

我有过这样的尝试:

  1. object OptionalConverter {
  2. implicit val string2Long = (s: String) => s.toLong
  3. implicit val string2Int = (s: String) => s.toInt
  4. implicit def convert[A, B](input: Option[A])(implicit mapper: A => B): Option[B] = input.flatMap(i => Try(mapper(i)).toOption)
  5. }

但是它不工作,我有点卡住了。
我不确定我的用法是否可行,或者我是否必须使它至少沿着val result = convert[Option[Long]](Option("123"))的行才能编译。
任何提示赞赏!

6kkfgxo0

6kkfgxo01#

它不起作用的原因是你有冲突的暗示。因为有不止一种方法可以将Option[String]转换为Option[Int],所以编译器无法知道应该使用哪种方法,也不会使用任何方法。
你可以在使用-Yno-predef编译器选项时看到它:

  1. import scala.language.implicitConversions
  2. import scala.util.Try
  3. object OptionalConverter {
  4. import scala.Predef.augmentString
  5. implicit val string2Long: String => Long = java.lang.Long.parseLong(_)
  6. //implicit val string2Int: String => Int = java.lang.Integer.parseInt(_)
  7. implicit def convert[A, B](input: Option[A])(implicit
  8. mapper: A => B
  9. ): Option[B] = input.flatMap(i => Try(mapper(i)).toOption)
  10. }
  11. import OptionalConverter._
  12. val input: Option[String] = Option("123")
  13. val longOpt: Option[Long] = input
  14. scala.Predef.println(longOpt)
  15. //val intOpt: Option[Int] = Option("123")
  16. //scala.Predef.println(intOpt)

(see:Scastie)。
这段代码编译并打印您所期望的内容。然而,在这方面,

  • 如果你取消注解implicit val string2Int: String => Int,它将停止工作-也许,因为你可以将Long转换为Int(反之亦然),这个原生的implicit A => B变得模糊不清。
  • 如果将string2Int注解掉,但删除了-Yno-predef,则从scala.Predef进行隐式转换将与string2Long发生冲突,并使其变得不明确
  • 同时你不能只启用Predef而注解掉string2intstring2Long,它也不起作用。

这突出了隐式转换有多少陷阱,以及它们以不可预测的方式发生冲突的容易程度(这并不像它们导致编译代码不应该编译,因为它们的混合导致不必要的行为)。
出于这个原因,最好使用带有类型类的某种扩展方法,因为它将基于隐式的行为约束到单个用例中,这有助于调试和驯服行为。

展开查看全部
ubbxdtey

ubbxdtey2#

使用非常一般的隐式几乎总是一个坏主意,因为它可能导致非常不可预测的结果。
与其使用裸A => B作为隐式转换器,不如将此转换行为封装在它自己的构造中。这将防止由非预期转换器候选者引入的不可预测性和错误。
因此,您可以定义类似OptionConverter trait的东西,然后在代码中使用它implicitly

  1. trait OptionConverter[A, B] {
  2. def convert(o: Option[A]): Option[B]
  3. }
  4. object OptionConverter {
  5. implicit val intToString: OptionConverter[Int, String] = new OptionConverter[Int, String] {
  6. override def convert(o: Option[Int]): Option[String] = o.map(_.toString())
  7. }
  8. implicit val stringToString: OptionConverter[String, Int] = new OptionConverter[String, Int] {
  9. override def convert(o: Option[String]): Option[Int] = o.map(s => Integer.parseInt(s))
  10. }
  11. }
  12. implicit class OptionConverterOps[A](o: Option[A]) {
  13. def convert[B](implicit oc: OptionConverter[A, B]): Option[B] = oc.convert(o)
  14. }
  15. val intOption = Option("10").convert[Int]
  16. val stringOption = Option(10).convert[String]
展开查看全部

相关问题