在Scala中有可能具有联合类型的集合。这里讨论的工会类型有几种方法最高的答案感觉最本地化,我有这样的东西:
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,任何]的映射,有没有办法告诉Scala编译器这是映射[String,StringerNumber]
编辑:
我不认为使用上面的方法可以创建字符串或联合的集合。我认为它需要是联合类型的另一种方法,因为上述类似于超载方法,而不是类型系统中的真正联合类型
运行时联合类型的最接近的仿真,您可以在当前版本的Scala中执行,以包装联合类型的类型,如果类别类别扩展了一些密封的特征。它是Beriplate-Y,并在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))
scala已经具有内置的case
-Classes,能够代表其他类型的任意标记的分离工会。
在您的情况下,定义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]
您复制的答案的一部分尚未完成。匹配有另一部分。它表明这种类型的工会在运行时起作用。因此,通常您会混合两种不同的内容:编译时间类型联合(您还提到了您提到的问题,最初由Miles Sabin在此处撰写(,并且会影响编译器检查和运行时类型检查。
。因此,一旦您使用运行时方法,Scala编译器就不了解此联盟,并建议使用任何
你应该写
val m: Map[String, StringOrNumber[_]] = ...
此功能现在正在Dotty中开发中。我知道那就像
Class[T1 | T2]
和
Class[T1 & T2]
,但是Dotty将在明年提供。现在,您可以使用您的方法,但这有点棘手,需要隐含。您也可以尝试任何一种类型(仅当您有2种通用类型时(,并且还可以注意Scalaz库。这都是关于类型级的编程。
您是否尝试过:
val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
在这种情况下,您可能还需要明确构建StringOrNumber
实例才能使其正常工作。
让我使用违反和键入约束向您介绍我的解决方案:
//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或字符串的扩展。在此处输入的值被检查为"违反"与Int with String
。
有一种解决方案的方法,您必须规避类型的推理,并在类型参数中提供基类,例如:
sizeOf[String](someExtendOfString)
Scala 3此后已发布本机对工会类型的支持。
type StringOrNumber = String | Int