我读了an answer on SO where someone said that scala enumerations are useless,如果你真的需要,你应该使用java枚举。
虽然我之前使用过java枚举,但我不能说我完全理解它们,因为我在日常工作中没有使用java编码。
有人可以解释scala和java枚举之间的区别,以及scala枚举中的缺点究竟在哪里?
答案 0 :(得分:8)
Scala枚举的主要缺点是难以向其添加方法和字段。在Java中,将枚举用作具有固定数量实例的类是非常简单的(例如,为什么它们是实现单例的好选择。)
然而,在Scala中,Enums只是一组可能的值;向它们添加方法和字段是一个更加hacky的过程。因此,如果你想要从枚举中获得除了微不足道的行为之外的任何东西,Java枚举是一个更方便的工具。
例如,Java枚举可能如下所示:
public enum Month{
january(31),
february(28),
...
december(31);
public final int daysInMonth;
Month(int daysInMonth){
this.daysInMonth = daysInMonth;
}
}
但是,在Scala中,你必须这样做:
object Month extends Enumeration{
protected case class Val(val daysInMonth:Integer) extends super.Val{}
implicit def valueToMonth(x:Value) = x.asInstanceOf[Val]
val january = Val(31)
val february = Val(28)
....
}
这在一个像这样的简单示例中看起来并不坏,但它确实增加了一些令人困惑的语法,并增加了另一个类的开销,需要隐式转换为enum的大多数用法。
我看到Java枚举的主要优点是你写的正是你的意思;一个枚举,上面有一些方法和字段。在斯卡拉,你写的不是你的意思;你需要创建一个枚举,它包含一个具有你想要的方法和字段的类,并且还定义了从该类到枚举的转换。表达同样的想法是一种不那么惯用的方式。
正如所指出的in the comments,Scala确实提供了Case Classes,在很多情况下可以用作枚举的替代方法,并且语法更清晰。但是在某些情况下,Case Classes还不够(例如当你想迭代所有值时),所以常规枚举仍然有它们的位置。 更正:使用宏确实可以迭代Case Classes,但这样做有其自身增加的复杂性,而枚举(在Scala和Java中)更容易迭代。
答案 1 :(得分:3)
Scala的Enumeration的主要优点是语法的规律性。
如果要向枚举元素添加行为,只需扩展其Val
类。
Odersky喜欢将它们作为最简单的用例命名为int-valued常量。他刚刚在邮件列表上承诺,这是第一次提供更好的支持。
但我recently used他们要替换字符串列表,因为值集是有点设置的,比查找字符串组更好。
而不是
val stuff = List("foo", "bar", "baz")
object stuff extends Enumeration { val foo, bar, baz = Value }
或
scala> object stuff extends Enumeration { val foo, bar, baz = Value }
defined object stuff
scala> val junk = new Enumeration { val foo, bar, baz = Value }
junk: Enumeration{val foo: this.Value; val bar: this.Value; val baz: this.Value} = 1
scala> stuff.values contains junk.foo
<console>:10: error: type mismatch;
found : junk.Value
required: stuff.Value
stuff.values contains junk.foo
^
行为:
scala> trait Alias { def alias: String }
defined trait Alias
scala> object aliased extends Enumeration {
| class Aliased extends Val with Alias {
| def alias = toString.permutations.drop(1).next }
| val foo, bar, baz = new Aliased }
defined object aliased
scala> abstract class X { type D <: Enumeration
| def f(x: D#Value) = x match { case a: Alias => a.alias
| case _ => x.toString } }
defined class X
scala> class Y extends X { type D = aliased.type }
defined class Y
scala> new Y().f(aliased.bar)
res1: String = bra
scala> new Y().f(stuff.foo)
<console>:13: error: type mismatch;
found : stuff.Value
required: aliased.Value
new Y().f(stuff.foo)
^
scala> new X { type D = junk.type }.f(junk.foo)
warning: there was one feature warning; re-run with -feature for details
res4: String = foo
ValueSet
有点设置:
scala> stuff.values contains aliased.bar
<console>:11: error: type mismatch;
found : aliased.Aliased
required: stuff.Value
stuff.values contains aliased.bar
^
scala> stuff.foo + aliased.bar
<console>:11: error: type mismatch;
found : aliased.Aliased
required: stuff.Value
stuff.foo + aliased.bar
^
scala> stuff.foo + stuff.bar
res8: stuff.ValueSet = stuff.ValueSet(foo, bar)
其他似乎适用于今天的Scala:
scala> def f[E <: Enumeration](e: E)(v: e.Value) = e.ValueSet.empty + v
f: [E <: Enumeration](e: E)(v: e.Value)e.ValueSet
scala> f(stuff)(stuff.foo)
res14: stuff.ValueSet = stuff.ValueSet(foo)
scala> def g[E <: Enumeration](e: E)(a: Any) = a match { case _: e.Value => true case _ => false }
g: [E <: Enumeration](e: E)(a: Any)Boolean
scala> g(stuff)(stuff.foo)
res15: Boolean = true
scala> g(stuff)(junk.foo) // checking outer pointers
warning: there was one feature warning; re-run with -feature for details
res16: Boolean = false
scala> g(stuff)(aliased.foo)
res17: Boolean = false
看起来Scala是not entirely friendly to Java enums:
scala> Thread.State.NEW.ordinal
[snip]
scala.reflect.internal.FatalError:
Unknown type: <notype>(NEW), <notype> [class scala.reflect.internal.Types$UniqueConstantType, class scala.reflect.internal.Types$NoType$] TypeRef? false
while compiling: <console>
during phase: icode
library version: version 2.11.2
compiler version: version 2.11.2
reconstructed args:
last tree to typer: Apply(method ordinal)
tree position: line 8 of <console>
tree tpe: Int
symbol: final method ordinal in class Enum
symbol definition: final def ordinal(): Int (a MethodSymbol)
symbol package: java.lang
symbol owners: method ordinal -> class Enum
call site: constructor $read$$iw$$iw in package $line4