Enum.Value vs Enum#Value

时间:2014-12-31 02:53:50

标签: scala enums

我正在学习Scala来自Java背景,我发现的第一件事与Java有很大不同的是Enums。我已经设法通过反复试验来完成我想要的所有事情,但是我希望能够更好地理解我一直在做的事情。

在Scala文档中,我通过扩展类Enumeration来创建枚举,并通过将它们设置为常量Value来添加值,例如:

object Label extends Enumeration{
  val NONE = Value
}

这与预期一致。我的问题不仅在于使用枚举,还在于使用自定义编写的枚举扩展的扩展。我写了一大堆代码作为机器学习类的一部分(现在结束),用它们的标签分隔数据(例如用于TDIDT)。在底部是它的一小部分,以便让我感到困惑。 Data对象是可运行的,只是为了试一试。

首先,在print语句中,我认为这是真的,但它不是

println(Label.NONE.equals(MessageLabel.NONE))//Thought this would be true, is false

为什么会这样?即使MessageLabel继承的NONE直接来自Label,类型系统是否坚持它们是不同的枚举值?


其次,更重要的是,我一直在Label.ValueLabel#Value之间来回走动。我发布的版本:

  • def splitByLabel[T <: Label#Value]
  • trait Labelable[T <: Label#Value]
  • abstract class Data[T <: Label#Value]
  • class Message( ... val label : MessageLabel.Value)

正确编译并运行。当我将所有#更改为.时,我在行splitByLabel(messages).foreach(a => println(a))上收到编译时错误,说明:

  

推断类型参数[MessageLabel.Value]不符合方法splitByLabel的类型参数边界[T&lt ;: Label.Value]

但当我将所有.更改为#时,我在行class Message(val index : Int, val s : Map[Double, Int], override val label : MessageLabel#Value) extends Data[MessageLabel#Value](label)上收到编译时错误,说明:

  

未找到:键入MessageLabel

很明显,两者之间存在差异,它们各自都具有特定的作用。有人能帮我理解有什么区别吗?谢谢!

/** Enum type all labels should extend. Guarantees access of universal NONE label */
class Label extends Enumeration{
  val NONE = Value
}
/** Singleton instance for accessing NONE */
object Label extends Label{}

/** Companion object to all data classes. Hosts helper methods and a runnable main method */
object Data{
  /** Returns a map of lists, each list is similarly labeled data. Map is label -> list of data */
  def splitByLabel[T <: Label#Value](elms : List[Labelable[T]]) : Map[T, List[Labelable[T]]] = {
    def f(acc : Map[T, List[Labelable[T]]], e : Labelable[T]) : Map[T, List[Labelable[T]]] = {
      if(acc.contains(e.label)){
        val l = acc(e.label)
        acc - e.label + ((e.label, (e :: l)))
      } else{
        acc + ((e.label, List(e)))
      }
    }
    elms.foldLeft(Map[T, List[Labelable[T]]]())(f) 
  }

  def main(args : Array[String]){
    println(Label.NONE.equals(MessageLabel.NONE))

    val messages : List[Message] = (0 to 10).toList.map(a => 
      new Message(a, Map(), if(a % 3 == 0) MessageLabel.HAM else MessageLabel.SPAM))
    splitByLabel(messages).foreach(a => println(a))
  }
}

/** Implementing classes can be labeled */
trait Labelable[T <: Label#Value]{ 
  /** Returns the label of this thing */
  val label : T
  /** The possible labelings for this thing */
  val labels : List[T]
}

abstract class Data[T <: Label#Value](override val label : T) extends Labelable[T]{
  override def toString(): String = {
    if (label != null) 
      label.toString
    else 
      "NO_LABEL"
  }
}

object MessageLabel extends Label{
  val HAM, SPAM = Value
}

/** An instance represents a sentence. */
class Message(val index : Int, val s : Map[Int, Double], override val label : MessageLabel.Value) 
    extends Data[MessageLabel.Value](label){
  /** Returns the possible labelings for a message */
  override val labels = MessageLabel.values.toList 

  /** Adds index to tostring at front */
  override def toString() : String = {
    index + "-" + super.toString
  }
}

2 个答案:

答案 0 :(得分:2)

这不是Enumeration特有的。

scala> class A { class B ; val None = new B }
defined class A

scala> class C extends A ; class D extends A
defined class C
defined class D

scala> val c = new C ; val d = new D
c: C = C@45fe3ee3
d: D = D@4cdf35a9

scala> c.None == d.None
res0: Boolean = false

没有人会指望这是真的。一个值在一个(超级)构造函数中初始化,另一个值在另一个(超级)构造函数中初始化。

此外,Value不是常数;它是一个功能,说,&#34;给我另一个价值。&#34;因此,您要为每个实例生成一个值。

在Java中,你无法在这个意义上扩展枚举。 To&#34;延伸&#34;是添加成员或增加扩展名,但子类化意味着子集或受限域。

这是一种优先考虑组合而不是继承的情况。鉴于一组工作日和周末日,我通过添加它们来获得,而不是通过延长工作日和周末。

Here是以路径依赖方式使用枚举的示例。

代码的另一个问题是:

scala> MessageLabel.NONE
res4: MessageLabel.Value = <Invalid enum: no field for #0>

https://issues.scala-lang.org/browse/SI-5147

答案 1 :(得分:2)

Label#ValueValue类型中的Label类型。 Label.Value Value中的Label类型。 (这有点令人困惑,因为你有class Labelobject Label(即一个值))。因此MessageLabel.ValueLabel#Value,因为MessageLabel是类型(classLabel的实例。但它不是Label.Value,因为MessageLabel不是值objectLabel。并且没有MessageLabel#Value,因为没有class MessageLabel(或特征)。

(FWIW我发现scala Enumeration非常混乱,并且更喜欢在我的Scala代码中使用Java枚举)