当参数限于Scala中的AnyRef和AnyVal时的冲突方法

时间:2018-12-04 10:35:59

标签: scala generics

Scala编译器将以下两个map函数检测为相互冲突的重复项:

class ADT {
    def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
    def map[Output >: Null <: AnyRef](f: Int => Output): List[Output] = ???
}

Output参数的类类型不同。第一个限制为AnyVal,第二个限制为AnyRef。我该如何区分它们?

3 个答案:

答案 0 :(得分:4)

问题并没有使AnyValAnyRef有所区别,而没有解决两个方法签名在擦除后变得相同的事实。

这是解决此类问题的巧妙方法。它与@ som-snytt的操作类似,但是更加通用,因为它也适用于其他类似情况(例如def foo(f: Int => String): String = ??? ; def foo(f: String => Int): Int = ???等):

class ADT {
  def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
  def map[Output >: Null <: AnyRef](f: Int => Output)(implicit dummy: DummyImplicit): List[Output] = ???
}

最可爱的是,它“开箱即用”。显然,DummyImplicit是标准库的一部分,并且您始终在范围内。 通过向列表中添加更多虚拟变量,您也可以通过这种方式实现两个以上的重载。

答案 1 :(得分:2)

scala 2.13.0-M5> :pa
// Entering paste mode (ctrl-D to finish)

object X {
    def map[Output <: AnyVal](f: Int => Output) = 1
    def map[O](f: Int => O)(implicit ev: O <:< AnyRef) = 2
}

// Exiting paste mode, now interpreting.

defined object X

scala 2.13.0-M5> X.map((x: Int) => x*2)
res0: Int = 1

scala 2.13.0-M5> X.map((x: Int) => "")
res1: Int = 2

答案 2 :(得分:2)

您可以为该map方法使用类型类。

使用您的确切示例:

trait MyTC[Output]{
  def map(f: Int => Output): List[Output]
}

object MyTC{
  def apply[A](a : A)(implicit ev : MyTC[A]) : MyTC[A] = ev 

  implicit def anyRefMyTc[A <: AnyRef] : MyTC[A]  = new MyTC[A]{
    def map(f: Int => A): List[A] = { println("inside sub-AnyRef"); List.empty }
  }
  implicit def anyValMyTc[A <: AnyVal] : MyTC[A] = new MyTC[A]{
    def map(f: Int => A): List[A] = { println("inside sub-AnyVal"); List.empty }
  }
}

import MyTC._

val r1 = Option("Test1")
val r2 = List(5)
val v1 = true
val v2 = 6L

// The functions here are just to prove the point, and don't do anything.
MyTC(r1).map(_ => None)
MyTC(r2).map(_ => List.empty)
MyTC(v1).map(_ => false)
MyTC(v2).map(_ => 10L)

那将打印:

  

内部sub-AnyRef
  内部subAnyRef
  在子AnyVal内部
  在subAnyVal内部

这种方法的优势在于,如果您随后选择只针对某种特定类型(例如,您想对Option[String]做特定的事情)来进一步专门化行为,则可以轻松地做到这一点:

// This is added to MyTC object 
implicit val optMyTc : MyTC[Option[String]] = new MyTC[Option[String]]{
    def map(f: Int => Option[String]): List[Option[String]] = { println("inside Option[String]"); List.empty }
  }

然后,重新运行代码将打印:

  

内部选项[String]
  内部subAnyRef
  在子AnyVal内部
  在subAnyVal内部