1

假设我有一些函数应该采用 Ints 序列或 Strings 序列。

我的尝试:

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])

但这行不通。

有没有办法优雅地对类型施加我所需的约束?

4

3 回答 3

3

我觉得您应该为两者编写一个单独的函数,但是还有其他方法可以做到:

在 Dotty 中,您可以只使用联合类型,这是我在这里推荐的:

编辑:根据 Alexey Romanov 的建议,替换Seq[AnyVal | String]Seq[AnyVal | String],这是错误的,

def foo(seq: Seq[AnyVal] | Seq[String]): Unit = {
  println(seq)
}

斯卡斯蒂


如果您不使用 Dotty,您仍然可以在 Scala 2 中使用几个可重复使用implicit def的 s


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

斯卡斯蒂


正如 Luis Miguel Mejía Suárez 建议的那样,您也可以使用类型类来完成它,但我不建议将它用于如此琐碎的任务,因为您仍然必须为每种类型定义 2 个函数,以及一个 trait、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))

在斯卡斯蒂

于 2020-06-13T19:44:08.023 回答
3

注意上限之间的差异

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
于 2020-06-13T19:44:51.983 回答
1
def f1( seq: Seq[Any] ): Unit = 
  println( seq( 0 ) )

这是可行的,因为 Int 和 String 的最不常见的超类型(两者的超类型的“最低”类型)将是Any,因为 Int 是AnyVal(即“原语”)的子类型,而 String 是AnyRef(即堆对象)。f1不以任何方式依赖于列表的内容,并且它的返回类型 ( Unit) 不是多态的,所以我们根本不需要类型参数。

于 2020-06-13T19:43:37.110 回答