假设我有一些函数应该接受一个整数序列或一个字符串序列。
我的尝试:
object Example extends App {
import scala.util.Random
val rand: Random.type = scala.util.Random
// raw data
val x = Seq(1, 2, 3, 4, 5).map(e => e + rand.nextDouble())
val y = Seq("chc", "asas")
def f1[T <: AnyVal](seq: Seq[T]) = {
println(seq(0))
}
// this works fine as expected
f1(x)
// how can i combine
f1(y)
}
如何添加它以使用字符串?
如果我将方法签名更改为:
def f1[T <: AnyVal:String](seq: Seq[T])
但这行不通。
有没有办法优雅地对类型施加我所需的约束?
注意上限之间的差异
A <: C
和上下文绑定
A : C
所以类型参数子句[T <: AnyVal : String]
没有多大意义。此外,String
等类型很少(或从不(用作上下文边界。
这是一个类型类方法
trait EitherStringOrAnyVal[T]
object EitherStringOrAnyVal {
implicit val str: EitherStringOrAnyVal[String] = new EitherStringOrAnyVal[String] {}
implicit def aval[T <: AnyVal]: EitherStringOrAnyVal[T] = new EitherStringOrAnyVal[T] {}
}
def f1[T: EitherStringOrAnyVal](seq: Seq[T]): Unit = {
println(seq(0))
}
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
或通用类型约束方法
object Foo {
private def impl[T](seq: Seq[T]): Unit = {
println(seq(0))
}
def f1[T](seq: Seq[T])(implicit ev: T =:= String): Unit = impl(seq)
def f1[T <: AnyVal](seq: Seq[T]): Unit = impl(seq)
}
import Foo._
f1(Seq(1)) // ok
f1(Seq("a")) // ok
f1(Seq(Seq(1))) // nok
我觉得你应该为两者编写一个单独的函数,但还有其他方法可以做到这一点:
在 Dotty 中,你可以只使用联合类型,这是我在这里推荐的:
编辑:根据阿列克谢·罗曼诺夫的建议,将Seq[AnyVal | String]
替换为Seq[AnyVal | String]
,这是错误的,
def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
println(seq)
}
斯卡斯蒂
如果你没有使用Dotty,你仍然可以在Scala 2中使用几个可重用的implicit def
来做到这一点
class Or[A, B]
implicit def orA[A, B](implicit ev: A): Or[A, B] = new Or
implicit def orB[A, B](implicit ev: B): Or[A, B] = new Or
def foo[T](seq: Seq[T])(implicit ev: Or[T <:< AnyVal, T =:= String]): Unit =
println(seq)
foo(Seq("baz", "waldo"))
foo(Seq(23, 34))
foo(Seq(List(), List())) //This last one fails
斯卡斯蒂
你也可以用 typeclass 来做,正如 Luis Miguel Mejía Suárez 所建议的那样,但我不建议将其用于如此微不足道的任务,因为您仍然必须为每种类型定义 2 个函数,以及一个特征、2 个隐式对象和一个可以使用该特征实例的函数。您可能不希望此模式仅处理 2 种不同的类型。
sealed trait DoSomethingToIntOrString[T] {
def doSomething(t: Seq[T]): Unit
}
implicit object DoSomethingToAnyVal extends DoSomethingToAnyValOrString[AnyVal] {
def doSomething(is: Seq[AnyVal]): Unit = println(is)
}
implicit object DoSomethingToString extends DoSomethingToIntOrString[String] {
def doSomething(ss: Seq[String]): Unit = println(ss)
}
def foo[T](t: Seq[T])(implicit dsis: DoSomethingToIntOrString[T]): Unit = {
dsis.doSomething(t)
}
foo(Seq("foo", "bar"))
foo(Seq(1, 2))
在斯卡斯蒂
def f1( seq: Seq[Any] ): Unit =
println( seq( 0 ) )
这是有效的,因为Int 和 String 最不常见的超类型(作为两者超类型的"最低"类型(将是Any
,因为 Int 是AnyVal
的子类型(即"基元"(和字符串是AnyRef
的子类型(即"堆对象"(。f1
不以任何方式依赖于列表的内容,并且其返回类型 (Unit
( 中不是多态的,所以我们根本不需要类型参数。