2014-12-31 36 views
0

我正在学习来自Java背景的Scala,并且我发现的第一件事情与Java的作品明显不同的是Enums。我试图通过反复试验完成我想要的一切,但我很想更好地理解我一路上正在做的事情。Enum.Value vs Enum#Value

从斯卡拉文档,我被告知通过扩展类Enumeration创建一个枚举,并通过平等将它们设置为常数Value增加值,例如:

object Label extends Enumeration{ 
    val NONE = Value 
} 

这工作有关预期。我的麻烦来自于不仅使用枚举,而且使用自定义书写枚举扩展的扩展。我写了一段代码作为机器学习课程的一部分(现在已经结束),通过它们的标签分隔数据(例如用于TDIDT)。底部是它的一小部分,以便让我感到困惑。 Data对象是可运行的,只是为了试用它。

首先,在打印语句,我认为这将是真实的,但它不是

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)

编译和运行正常。当我改变所有的# s到.,我上线splitByLabel(messages).foreach(a => println(a))编译时错误,他说:

推断类型参数[MessageLabel.Value]不符合方法splitByLabel的类型参数界限[T <: Label.Value]

但是,当我改变所有的. s到# S,我上线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

这不是特定于枚举。

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 = [email protected] 
d: D = [email protected] 

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

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

另外,Value不是常数;这是一个函数,说:“给我另一个价值。”所以你为每个实例生成一个值。

在Java中,从这个意义上说,不能扩展枚举。 “扩展”是添加成员或增加扩展名,但是子类化意味着一个子集或受限制的域。

这是一种比继承更喜欢构图的情况。给定一组平日和周末日,我可以通过添加它们来获得全天,而不是通过在周末延长周日。

Here是以路径相关的方式使用枚举的示例。

与代码的另一个问题,因为它代表:

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

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

2

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

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