一般处理枚举的Scala类

时间:2013-01-22 03:33:53

标签: scala generics types parameters enums

我想创建一个包含枚举值的泛型类,并且还允许访问枚举的可能值。例如,可以考虑属性编辑器 - 您需要知道属性的当前值,并且还需要能够知道属性的其他值是合法的。并且不应提前知道枚举的类型,您应该能够使用任何类型的枚举。

我的第一个想法是这样的:

class EnumerationProperty[T <: Enumeration](value:T)

然而,这并不起作用,因为对于枚举T不是一个类型,它是一个对象。我尝试的其他变化是:

class EnumerationProperty[T <: Enumeration](value:T.Value)
class EnumerationProperty[T <: Enumeration.Value](value:T)

(我不会详细说明为什么这些不能正常工作,因为我怀疑这些原因并不有趣。)

3 个答案:

答案 0 :(得分:5)

问题的第一部分。您可以像这样定义通用枚举值的持有者:

case class EnumerationProperty[T <: Enumeration#Value](value:T)

但我不知道如何在没有显式传递Enumeration对象的情况下获取所有枚举值。枚举具有values()方法来获取所有值。 Enumeration#Value链接到Enumeration,但具有私有访问权限

答案 1 :(得分:3)

在您的用例(属性编辑器)中,我认为最好的解决方案是依赖scala 2.10中提供的scala反射。给定一个类TypeTag,您可以获得其所有成员及其值的完整类型(不删除),并从中填充您的属性编辑器。对于枚举,请使用TypeTag使用以下方法获取其值: Using Scala 2.10 reflection how can I list the values of Enumeration?


现在,也许你不想要或不能使用scala反射,从现在开始,我认为情况就是如此。如果这是真的那么你正在打开一整套蠕虫:) TL; DR:使用标准Enumeration是不可能的,因此您可能必须明确地包装枚举值,如Ptharien's Flame&#39;的回答(或者你自己的Enumeration分叉)。下面我详细介绍了我的所有尝试,请耐心等待。

不幸的是,由于某些未知原因(虽然我怀疑它与序列化问题有关),Enumeration.Value没有指向其Enumeration实例的字段。鉴于如何实现Enumeration,这实现起来是微不足道的,但当然我们没有发言权,除了分配Enumeration和修改我们的版本(这实际上是我为此目的所做的,加上为...添加适当的支持序列化和反思 - 但是我更加努力了。)

如果我们无法修改Enumeration,也许我们可以延长它?再看看实现,这样的事情似乎有用:

class EnumerationEx extends Enumeration {
  protected class ValEx(i: Int, name: String) extends Val(i, name) {
    @transient val enum: Enumeration = EnumerationEx.this
  }
  override protected def Value(i: Int, name: String): Value = new ValEx(i, name)  
}

object Colors extends EnumerationEx {
  val Red, Green, Blue = Value
}

缺点是它只适用于明确扩展EnumerationEx而不是Enumeration的枚举,但它总比没有好。

不幸的是,这不能简单编译因为def Value ...Enumeration中被声明为final,所以无法覆盖它。 (再次注意,分叉Enumeration将允许循环使用。实际上,为什么不这样做,因为我们已经沿着使用自定义枚举anwyay的路径。我将让你判断。)

所以这是另一种看法:

class EnumerationEx extends Enumeration {
  class ValueWithEnum( inner: Value ) {
    @transient val enum: Enumeration = EnumerationEx.this
  }
  implicit def valueToValue( value: Value ): ValueWithEnum = new ValueWithEnum( value )
}

确实正常运作。或者看起来如此。

scala> object Colors extends EnumerationEx {
     |   val Red, Green, Blue = Value
     | }
defined module Colors

scala> val red = Colors.Red
red: Colors.Value = Red

scala> red.enum.values
res58: Enumeration#ValueSet = Colors.ValueSet(Red, Green, Blue)

万岁?不,因为从ValueValueWithEnum的转换仅在访问red.enum时完成,而不是在Colors枚举实例化时完成。换句话说,当调用enum时,编译器需要知道枚举的确切静态类型(编译器必须静态地知道红色的类型是Colors.Value,而不仅仅是Enumeration# Value })。在你提到的用例(属性编辑器)中你只能依赖于java反射(我已经假设你不会使用scala反射)来获取枚举值的类型,而java反射只会给你Enumeration#Val {延伸Enumeration#Value)作为Colors.Red的类型。所以基本上你被困在这里。 你最好的选择是首先使用scala反射。

答案 2 :(得分:0)

class EnumProps[E <: Enumeration](val e: E)(init: e.Value) {...}

然后,您可以使用ee.Value来实现该课程。