我正在尝试在Scala中实现一些有效的枚举。我想使用案例类来做到这一点,以便编译器能够检测任何非详尽的模式匹配。
这在基本形式下工作正常,例如:
sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
...
def test (x : HorizontalAlignment) =
x match {
case Left => ...
...
}
然而,这并不理想,因为案例对象的名称很容易发生冲突:
sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
sealed abstract class VerticalAlignment
case object Top extends VerticalAlignment
case object Bottom extends VerticalAlignment
case object Center extends VerticalAlignment
case object AsIs extends VerticalAlignment
// "Center" and "AsIs" clash
显而易见的解决方案是将case对象放入单独的命名空间中:
sealed abstract class HorizontalAlignment {
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
}
sealed abstract class VerticalAlignment {
case object Top extends VerticalAlignment
case object Bottom extends VerticalAlignment
case object Center extends VerticalAlignment
case object AsIs extends VerticalAlignment
}
但是如何在匹配块中引用这些类?
它们不能用Java样式的点引用:
def test (x : HorizontalAlignment) =
x match {
case HorizontalAlignment.Left => 0 // error: not found: value HorizontalAlignment
}
“#”符号似乎也不起作用:
def test (x : HorizontalAlignment) =
x match {
case HorizontalAlignment#Left => 0 // error: '=>' expected but '#' found
}
这种形式也不起作用:
def test (x : HorizontalAlignment) =
x match {
case _ : HorizontalAlignment#Left => 0 // error: type Left is not a member of Test.HorizontalAlignment
}
这是有道理的,因为“Left”在这种情况下是一个实例而不是一个类型,我怀疑有一种简单的方法可以引用该类型。我能达到的最接近的是:
sealed abstract class HorizontalAlignment {
case class Left extends HorizontalAlignment
case class Right extends HorizontalAlignment
case class Center extends HorizontalAlignment
case class AsIs extends HorizontalAlignment
object Left
object Right
object Center
object AsIs
}
但是虽然这使匹配块编译正常但我找不到任何方法来实际引用这些对象,例如将此“枚举”的成员传递给函数。这是因为HorizontalAlignment是一个类型而不是一个对象,因此不可能使用字段访问来引用其中一个嵌套对象,另一方面,这些对象不是类型,所以不可能使用它来引用它们。 “#”符号。
有没有办法从类外部引用嵌套在类中的对象?
修改
到目前为止,我发现包对象是解决此问题的最佳方法。
package object HorizontalAlignment {
sealed abstract class HorizontalAlignment
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
}
package object VerticalAlignment {
sealed abstract class VerticalAlignment
case object Top extends VerticalAlignment
case object Bottom extends VerticalAlignment
case object Center extends VerticalAlignment
case object AsIs extends VerticalAlignment
}
object Test {
import HorizontalAlignment.HorizontalAlignment
import VerticalAlignment.VerticalAlignment
def test (x : HorizontalAlignment, y : VerticalAlignment) = {
x match {
case HorizontalAlignment.Left => ...
...
}
y match {
case VerticalAlignment.Top => ...
...
}
}
def testTest = test (HorizongalAlignment.Left, VerticalAlignment.Top)
}
但是,上述问题(对类中嵌套对象的访问)仍然存在。
答案 0 :(得分:11)
您不必使用包对象,这可能有一些额外的不良语义:常规的旧伴随对象也同样好:
sealed trait HorizontalAlignment
object HorizontalAlignment {
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
}
scala> def test (x : HorizontalAlignment) = x match {
| case HorizontalAlignment.Left => "got left"
| }
scala> test(HorizontalAlignment.Left)
res0: java.lang.String = got left
您遇到的问题是,由于HorizontalAlignment是抽象类,因此没有要取消引用的HorizontalAlignment实例。使用原始的命名空间公式,您需要实例化HorizontalAlignment实例,并且内部对象将特定于该实例。但是,由于HorizontalAlignment是密封的,因此无法在任何其他编译单元中创建这样的实例,而不是在其中定义的实例,因此实际上永远无法通过任何方式获取枚举值。
与Java不同,没有与类关联的“静态命名空间”;为了获得等价物,你必须使用一个伴侣对象。
答案 1 :(得分:2)
你已经明智地避开了这个结构,但要回答剩下的问题:要引用一个没有实例的类的成员,你将不得不求助于存在。
sealed abstract class HorizontalAlignment {
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
}
object Test {
type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }
def test(x: HorizontalAlignment): Int = x match {
case _: LeftOb => 0
}
}
不出所料(好吧,不出所料,如果你是我)试图在模式匹配中使用该类型会使编译器中的bejeezus崩溃。但原则上它是表达它的方式。
编辑:当我指出模式匹配器崩溃时,人们似乎分心了。让我以一种不那么崩溃的方式说明:a)这是表达有问题概念的唯一方法,b)它是有效的。
sealed abstract class HorizontalAlignment {
case object Left extends HorizontalAlignment
case object Right extends HorizontalAlignment
case object Center extends HorizontalAlignment
case object AsIs extends HorizontalAlignment
}
object Test {
type LeftOb = x.Left.type forSome { val x: HorizontalAlignment }
def f(x: Any) = x.isInstanceOf[LeftOb]
def main(args: Array[String]): Unit = {
val ha = new HorizontalAlignment { }
println(f(ha.Left))
println(f(ha.Right))
}
}
输出:
true
false