使用Any for Union类型是个好主意吗?

时间:2016-07-30 20:55:33

标签: scala

我想创建一个类生成器(适用于Avro模型)。我有一个问题,因为有时我生成的类的字段可能是许多不同类型中的一个,假设它可能是IntString或其他什么。简单的想法是使用类型Any创建该字段,并在运行时检查它是否正常。

问题是使用Any会丢弃scala类型系统的一些强大功能。编译时不会捕获代码中的某些错误(如果我将List提供给期望由String覆盖IntAny的函数。

例如:

让我对班级有这样的描述:

{"className" : "MyClass", "fields" : [
  { "fieldName" : "myField", "type" : ["String", "Int"]}
]}

从这个描述中,我创建了这个类:

class MyClass(myField: Any)

我想创建这样的东西:

class MyClass(myField: String or Int)

我应该停止使用Any吗?在scala社区中使用Any通常被认为是个好主意吗?

1 个答案:

答案 0 :(得分:3)

  

在scala社区中使用Any通常被认为是个好主意吗?

不。 Any表示没有类型信息,因此通常被视为不良做法。

在Scala中,您可以使用Either来表达联合类型,但如果联合中有很多可能的类型,它会变得很麻烦。例如:

class MyClass(myField: Either[String, Int]) {
  def doSomething = myField match {
    case Left(myStringField) => ???
    case Right(myIntField) => ???
  }
}

另一种可行的方法是在其类型中使MyClass通用:

class MyClass[A](myField: A)

但是,这并未对A的类型设置任何限制。

为了设置约束,例如使它成为类型的有限子集,您可以使用ad-hoc多态:

trait MyConstraint[A]
class MyClass[A: MyConstraint](myField: A)

现在new MyClass(myValue)不会编译,除非范围内有隐式MyConstraint[A]。现在,您可以使用隐式值

将要允许的类型列入白名单
implicit object IntConstraint extends MyConstraint[Int]
implicit object StringConstraint extends MyConstraint[String]

示例:

new MyClass(42) // ok, there's implicit evidence of MyConstraint[Int]
new MyClass("foo") // ok, there's implicit evidence of MyConstraint[String]
new MyClass(false) // won't compile, no implicit evidence of MyConstraint[Boolean]

在技术术语中,MyConstraint是一个类型类,用于在A的构造函数中优化类型MyClass

您可以通过要求为每个实例定义一组操作来进一步表征类型类。 E.g。

trait MyConstraint[A] {
  def mandatoryOp: A
}

implicit object IntConstraint extends MyConstraint[Int] {
  def mandatoryOp = 42
}

implicit object StringConstraint extends MyConstraint[String] {
  def mandatoryOp = "foo"
}

class MyClass[A](myField: A)(implicit ev: MyConstraint[A]) {
  def doSomething: A = ev.mandatoryOp 
}

请注意,A: MyConstraint只是要求类型为MyConstraint[A]的隐式参数的语法糖。在上一个示例中,我选择了显式语法,以便在范围内使用隐式参数ev