我正在学习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.Value
和Label#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
}
}
答案 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>
答案 1 :(得分:2)
Label#Value
是Value
类型中的Label
类型。 Label.Value
是值 Value
中的Label
类型。 (这有点令人困惑,因为你有class Label
和object Label
(即一个值))。因此MessageLabel.Value
是Label#Value
,因为MessageLabel
是类型(class
)Label
的实例。但它不是Label.Value
,因为MessageLabel
不是值object
)Label
。并且没有MessageLabel#Value
,因为没有class MessageLabel
(或特征)。
(FWIW我发现scala Enumeration
非常混乱,并且更喜欢在我的Scala代码中使用Java枚举)