小编典典

如何定义“类型析取”(联合类型)?

all

建议处理重载方法的双重定义的一种方法是用模式匹配替换重载:

object Bar {
   def foo(xs: Any*) = xs foreach { 
      case _:String => println("str")
      case _:Int => println("int")
      case _ => throw new UglyRuntimeException()
   }
}

这种方法要求我们放弃对参数的静态类型检查foo。能写会好很多

object Bar {
   def foo(xs: (String or Int)*) = xs foreach {
      case _: String => println("str")
      case _: Int => println("int")
   }
}

我可以接近Either,但有两种以上的类型会很快变得丑陋:

type or[L,R] = Either[L,R]

implicit def l2Or[L,R](l: L): L or R = Left(l)
implicit def r2Or[L,R](r: R): L or R = Right(r)

object Bar {
   def foo(xs: (String or Int)*) = xs foreach {
      case Left(l) => println("str")
      case Right(r) => println("int")
   }
}

看起来一个通用(优雅、高效)的解决方案需要定义Either3, Either4, ....
有没有人知道实现相同目的的替代解决方案?据我所知,Scala
没有内置的“类型析取”。另外,上面定义的隐式转换是否潜伏在标准库中的某个地方,以便我可以导入它们?


阅读 72

收藏
2022-07-17

共1个答案

小编典典

好吧,在特定情况下Any*,下面的这个技巧将不起作用,因为它不接受混合类型。但是,由于混合类型也不适用于重载,这可能是您想要的。

首先,使用您希望接受的类型声明一个类,如下所示:

class StringOrInt[T]
object StringOrInt {
  implicit object IntWitness extends StringOrInt[Int]
  implicit object StringWitness extends StringOrInt[String]
}

接下来,foo像这样声明:

object Bar {
  def foo[T: StringOrInt](x: T) = x match {
    case _: String => println("str")
    case _: Int => println("int")
  }
}

就是这样。你可以调用foo(5)or foo("abc"),它会工作,但尝试foo(true)它会失败。客户端代码可以通过创建
来绕过这一步StringOrInt[Boolean],除非如下Randall所述,您创建StringOrInt了一个sealed类。

它之所以有效,是因为T: StringOrInt意味着有一个 type 的隐式参数StringOrInt[T],并且因为 Scala
会在一个类型的伴随对象内部查看是否存在隐式以使请求该类型的代码工作。

2022-07-17