下面是关于如何定义union types in Scala的一组出色的答案。我一直在使用Miles Sabin对Union类型的定义,但有一个问题仍然存在。
如果直到运行时才知道类型,那么如何处理这些类型呢?例如:
trait inv[-A] {}
type Or[A,B] = {
type check[X] = (inv[A] with inv[B]) <:< inv[X]
}
case class Foo[A : (Int Or String)#check](a: A)
Foo(1) // Foo[Int] = Foo(1)
Foo("hi") // Foo[String] = Foo(hi)
Foo(2.0) // Error!
这个例子之所以有效,是因为参数A
在编译时是已知的,调用Foo(1)
实际上就是在调用Foo[Int](1)
。但是,如果参数A
直到运行时才知道,该怎么办?也许你正在解析一个包含Foo
数据的文件,在这种情况下,Foo
的类型参数直到你读取数据后才知道,在这种情况下没有简单的方法来设置参数A
。
我能想到的最好的解决方案是:
将
Foo
。在我的例子中,这是不可行的,因为我的case类实际上包含几十个联合类型,因此将有数百种类型组合进行模式匹配。(String or Int)
,因此您只需传递一个类型,当您使用它创建Foo
时,该类型将传递type类约束。然后返回Foo[_]
。这让Foo用户重新承担了确定每个字段的类型的责任(因为它们看起来是类型Any),但至少它推迟了知道类型的时间,直到字段被实际使用,在这种情况下,模式匹配似乎更容易处理。第二个解决方案如下所示:
def parseLine: Any // Parses data point, but can be either a String or
// Int, so returns Any.
def mkFoo: Foo[_] = {
val a = parseLine.asInstanceOf[Int with String]
Foo(a) // Passes type constraint now
}
在实践中,我最终使用了第二种解决方案,但我想知道是否有更好的方法可以做?
陈述问题的另一种方法是:返回Union类型意味着什么?函数只能返回单个类型,我们对Miles Sabin联合类型使用的技巧只适用于您传入的类型,而不适用于您返回的类型。
PS。对于上下文,为什么这在我的案例中是一个问题,是因为我正在从一个Json模式文件生成一组case-classes。Json自然支持联合类型,所以我想让我的case类也反映这一点。这在一个方向上效果很好:用户创建要序列化到Json的case-case。但在另一个方向上变得棘手:用户解析Json文件,以将一组填充的case类返回给它们。
https://stackoverflow.com/questions/38295995
复制相似问题