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
。我该如何区分它们?
答案 0 :(得分:4)
问题并没有使AnyVal
与AnyRef
有所区别,而没有解决两个方法签名在擦除后变得相同的事实。
这是解决此类问题的巧妙方法。它与@ 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内部