一般处理枚举的Scala类 [英] Scala class that handles enumerations generically

查看:147
本文介绍了一般处理枚举的Scala类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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

  class EnumerationProperty [T<:枚举](值:T)

但是,这不起作用,因为枚举T不是一个类型,它是一个对象。我尝试过的其他变体是:

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

(我赢了'详细了解为什么这些不起作用,因为我怀疑原因不是有趣的。)

解决方案

在你的用例(一个属性编辑器)我认为最好的解决方案是依靠Scala 2.10中的Scala反射。给定一个类的 TypeTag ,您可以获得所有成员的完整类型(无擦除)及其值,并从中填充属性编辑器。对于枚举,使用 TypeTag 使用以下方法获取其值:
使用Scala 2.10反思如何列出枚举的值?






现在,也许你不想要或不能使用scala反射,从现在开始我会假设是这种情况。如果这是真的,那么你正在打开一整套蠕虫:)
TL; DR:这不可能使用标准的枚举,所以你可能必须明确地包含枚举值,如Ptharien的Flame的答案所示(或者滚动你自己的枚举)。下面我详细说明我的所有尝试,请与我保持联系。



不幸的是,对于一些未知的原因(虽然我怀疑它与序列化问题有关) code> Enumeration.Value 没有字段指向其枚举实例。考虑到如何实现枚举,这实现是微不足道的,但是当然我们没有发言权,没有发现枚举和修改我们的版本(这实际上是我为此做的非常的目的,加上适当的支持序列化和反思 - 但我的讨论)。



如果我们不能修改枚举,也许我们可以扩展它?再看看这个实现,这样的东西似乎有效:

  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)
}

对象颜色扩展EnumerationEx {
val红色,绿色,蓝色=值
}

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



不幸的是,由于 def Value ... 枚举所以没有办法覆盖它。 (再次注意,分叉枚举将允许循环,实际上为什么不这样做,因为我们已经在使用自定义枚举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>对象颜色扩展EnumerationEx {
| val红色,绿色,蓝色=价值
| }
定义模块颜色

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

scala>红色,绿色,蓝色)
不好意思好的,因为从 Value ValueWithEnum 的转换仅在访问 red.enum时完成,而不是在实例化 Colors 枚举时。换句话说,当调用枚举时,编译器需要知道枚举的确切静态类型(编译器必须静态地知道红色的类型是颜色。值,而不仅仅是枚举#值)。而在你提到的用例(一个属性编辑器)中,你只能依靠java反射(我已经假设你不会使用scala反射)来获取一个枚举值的类型,而java的反射只会让你枚举#Val (扩展枚举#值)作为 Colors.Red 。所以基本上你被困在这里
你最好的选择是首先使用scala反射。


I want to create a generic class that holds the value of an enumeration, and also allows access to the possible values of the enumeration. Think of a property editor for example - you need to know the current value of the property, and you also need to be able to know what other values are legal for the property. And the type of enumeration should not be known in advance, you should be able to work with any kind of enumeration.

My first thought was something like this:

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

However, that doesn't work because for enumerations T isn't a type, it's an object. Other variations I have tried are:

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

(I won't go into the details of why these don't work because I suspect the reasons aren't interesting.)

解决方案

In your use case (a property editor) I think the best solution is to rely on scala reflection available in scala 2.10. Given a class's TypeTag, you can get the full type (without erasure) of all of its members plus their values, and from that populate your property editor. For enumerations, use the TypeTag to get their values using the following method: Using Scala 2.10 reflection how can I list the values of Enumeration?


Now, maybe you don't want or can't use scala reflection, and from now on I'll suppose this is the case. If that is true then you are opening a whole can of worm :) TL;DR: This is not possible using a standard Enumeration, so you'll probably have to explcitly wrap the enumeration values as shown in Ptharien's Flame's answer (or roll your own fork of Enumeration). Below I detail all my attempts, please bear with me.

Unfortunately, and for some unknown reason to me (though I suspect it has to do with serialization issues), Enumeration.Value has no field pointing to its Enumeration instance. Given how Enumeration is implemented, this would be trivial to implement, but of course wehave no say, short of forking Enumeration and modifying our version (which is actually what I did for this very purpose, plus to add proper support for serialization and reflection - but I diggress).

If we can't modify Enumeration, maybe we can just extend it? Looking at the implementation again, something like this would seem to work:

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
}

The downside would be that it only works for enumerations that explicitly extend EnumerationEx instead of Enumeration, but it would be better than nothing.

Unfortunately, this does not compile simply because def Value ... is declared final in Enumeration so there is no way to override it. (Note again that forking Enumeration would allow to circunvent this. Actually, why not do it, as we are already down the path of using a custom Enumeration anwyay. I'll let you judge).

So here is another take on it:

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

And indeed it works as expected. Or so it seems.

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)

Hooray? Well no, because the conversion from Value to ValueWithEnum is done only when accessing red.enum, not at the time of instantiation of the Colors enumeration. In other words, when calling enum the compiler needs to know the exact static type of the enumeration (the compiler must statically know that red's type is Colors.Value, and not just Enumeration# Value). And in the use case you mention (a property editor) you can only rely to java reflection (I already assumed that you won't use scala reflection) to get the type of an enumeration value, and java reflection will only give you Enumeration#Val (which extends Enumeration#Value) as the type of Colors.Red. So basically you are stuck here. Your best bet is definitly to use scala reflection in the first place.

这篇关于一般处理枚举的Scala类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆