遵循关于如何在 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
但是,如果直到运行时才知道参数怎么办?也许你正在解析一个包含 's 数据的文件,在这种情况下,直到你读取数据才知道Foo
类型参数。在这种情况下,Foo
没有简单的方法来设置参数。A
我能想出的最佳解决方案是:
对您已阅读的数据进行模式匹配,然后
Foo
根据该类型创建不同的 '。在我的情况下,这是不可行的,因为我的案例类实际上包含几十个联合类型,所以会有数百种类型的组合来进行模式匹配。将您刚刚阅读的类型转换为
(String or Int)
,因此您有一个要传递的类型,当您使用它创建Foo
时它会传递 Type Class 约束。然后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
}
在实践中,我最终使用了第二种解决方案,但我想知道是否有更好的方法可以做?
说明问题的另一种方式是:返回联合类型是什么意思?函数只能返回一个类型,而我们对 Miles Sabin 联合类型使用的技巧只对您传入的类型有用,而不是对您返回的类型有用。
PS。对于上下文,为什么在我的情况下这是一个问题,因为我正在从 Json 模式文件生成一组案例类。Json 自然支持联合类型,所以我想让我的案例类也反映这一点。这在一个方向上很有效:用户创建要序列化到 Json 的案例类。但在另一个方向上变得棘手:用户解析 Json 文件以将一组填充的案例类返回给他们。