scala中是否可以拥有联合类型的集合。所讨论的联合类型有一些方法here最受好评的答案感觉最本土,我有这样的事情:
sealed trait StringOrNumber[T]
object StringOrNumber {
implicit object IntWitness extends StringOrNumber[Int]
implicit object StringWitness extends StringOrNumber[String]
}
但是当我尝试制作包含两者的地图时
val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)
scala编译器将其视为[String,Any]的映射有没有办法告诉scala编译器这是一个map [String,StringOrNumber]
编辑:
我不认为使用上面的方法可以创建字符串或联合的集合。我认为它需要是一种联合类型的另一种方法,因为上面类似于重载方法而不是类型系统中的真正联合类型
答案 0 :(得分:3)
最接近的运行时联合类型仿真,您可以在当前版本的Scala中执行,是在扩展一些密封特征的case类中包装union的类型。它是样板文件,并在AnyRef
类型上添加了一个额外的包装层,但是它有效,它比仅使用Any
更好,您还可以从联合类型中添加隐式转换:
sealed trait StringOrNumber
object StringOrNumber {
case class IsNumber(i: Int) extends StringOrNumber
case class IsString(s: String) extends StringOrNumber
implicit def isNumber(i: Int): StringOrNumber = IsNumber(i)
implicit def isString(s: String): StringOrNumber = IsString(s)
}
现在您可以定义Map
:
scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))
答案 1 :(得分:3)
Scala已经内置了case
- 类,它们能够表示其他类型的任意标记的不相交联合。
在您的情况下,定义StringOrNumber
的最简单方法是:
sealed trait StringOrNumber
case class Num(n: Int) extends StringOrNumber
case class Str(s: String) extends StringOrNumber
val m: Map[String, StringOrNumber] = Map(
"str" -> Str("hellp"),
"int" -> Num(42)
)
for ((k, v) <- m) {
v match {
case Num(n) => println("It's an int: " + n)
case Str(s) => println("A string: " + s)
}
}
如果您不想为此创建额外特征,并且只有两种类型,请使用Either
:
type StringOrNum = Either[String, Int]
答案 2 :(得分:2)
您复制的部分答案尚未完成。还有另一部分匹配。并且它表明这种类型的联合在运行时工作。 所以一般来说,你混合了两个不同的东西:编译时类型联合(这也是你提到的问题,最初是由Miles Sabin here编写的),它影响编译器检查和运行时类型检查。
所以,只要你使用运行时方法,scala编译器就是不理解这个联合,并建议使用Any
答案 3 :(得分:1)
你应该写
val m: Map[String, StringOrNumber[_]] = ...
此功能目前正在Dotty中开发。据我所知,它会像
Class[T1 | T2]
和
Class[T1 & T2]
但是dotty将在明年推出。 现在,你可以使用你的方法,但它有点棘手,需要暗示。 您也可以尝试使用任何一种类型(仅当您拥有2种通用类型时),并且您还可以关注scalaz库。这完全是关于类型级编程的。
答案 4 :(得分:0)
你有没有尝试过:
val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
在这种情况下,您可能还需要显式构造StringOrNumber
实例以使其正常工作。
答案 5 :(得分:0)
让我向您介绍我的解决方案,使用逆变和类型约束:
//Add this to your util library
trait Contra[-A]
type Union[A,B] = Contra[A] <:< Contra[B]
//And see a usage example below
@implicitNotFound("Only Int or String can be sized")
type Sizeable[T] = Union[T, Int with String]
def sizeOf[T: Sizeable](sizeable: T): Int = {
sizeable match {
case i: Int => i
case s: String => s.length
}
}
此解决方案的问题是此处不接受Int或String的扩展。这里输入的值被检查约束为&#34;逆变&#34;到Int with String
。
有一种解决方法,你必须绕过类型推断,并在type参数中提供基类,如下所示:
sizeOf[String](someExtendOfString)