2014-08-29 5 views
5

Ich lese an answer on SO where someone said that scala enumerations are useless, und Sie sollten nur Java-Enumerationen verwenden, wenn Sie wirklich brauchen.Unterschiede zwischen Scala und Java-Enumerationen

Obwohl ich Java-Aufzählungen zuvor verwendet habe, kann ich nicht sagen, dass ich sie vollständig verstehe, da ich während meines Tagesjobs nicht in Java code.

Kann jemand die Unterschiede zwischen Scala und Java Aufzählungen erklären, und wo genau die Mängel in Scala Enums sind?

+0

Scala Enumeration nicht, dass hinter ist. http://stackoverflow.com/questions/21537148/scala-java-enumerations Es ist nur nicht so bequem wie Scala Fall Klassen/Objekte. – Naetmul

Antwort

8

Die Hauptschwäche von Scala enums ist die Schwierigkeit, Methoden und Felder hinzuzufügen. In Java ist es trivial, eine Enumeration als Klasse mit einer festen Anzahl von Instanzen zu verwenden (was zum Beispiel der Grund ist, warum sie für die Implementierung von Singletons geeignet sind).

In Scala sind Enums jedoch nur eine Reihe möglicher Werte; Hinzufügen von Methoden und Feldern zu ihnen ist ein viel hacky Prozess. Wenn Sie also etwas anderes als triviales Verhalten von der Enumeration wünschen, sind Java-Enums ein viel praktischeres Werkzeug.

Zum Beispiel ein Java Enum könnte wie folgt aussehen:

public enum Month{ 
    january(31), 
    february(28), 
    ... 
    december(31); 

    public final int daysInMonth; 
    Month(int daysInMonth){ 
     this.daysInMonth = daysInMonth; 
    } 
} 

jedoch in Scala, würden Sie dies tun müssen:

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

Dies sieht nicht so schlecht in einem trivialen Beispiel wie folgt, fügt jedoch eine verwirrende Syntax hinzu und fügt den Overhead einer anderen Klasse hinzu, die implizit für die meisten Verwendungen der Enumeration konvertiert werden muss.

Der Hauptvorteil, den ich zum Java enum sehe, ist, dass Sie genau schreiben, was Sie meinen; ein enum, mit einigen Methoden und Feldern darauf. In Scala schreibst du etwas anderes als was du meinst; Sie müssen eine Enumeration erstellen, die eine Klasse enthält, die die gewünschten Methoden und Felder enthält, und außerdem eine Konvertierung von dieser Klasse in die Enumeration definieren. Es ist eine weniger idiomatische Art, dieselbe Idee auszudrücken.

Wie bereits erwähnt in the comments, Scala bietet Case Classes, die als Alternative zu Enums in vielen Fällen verwendet werden kann, und haben eine viel sauberere Syntax. Aber es gibt immer noch Situationen, in denen Case-Klassen nicht ausreichen (zB wenn Sie über alle Werte hinweg iterieren möchten), so dass reguläre Enums immer noch ihren Platz haben. Korrektur: die Verwendung von Makros macht es möglich, über Case-Klassen zu iterieren, aber dies hat seine eigene zusätzliche Komplexität, während enums (sowohl in Scala als auch in Java) viel einfacher zu iterieren sind.

+1

Es ist erwähnenswert, dass eine dritte Lösung die Verwendung von Fallobjekten ist [http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala]. –

+0

Und Iteration über versiegelte Eigenschaft ist möglich mit Makros - http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala – Gavin

3

Der Hauptvorteil von Scala's Enumeration ist die Regelmäßigkeit der Syntax.

Wenn Sie den Elementen Ihres Enums ein Verhalten hinzufügen möchten, erweitern Sie einfach seine Val Klasse.

Odersky mag sie für ihren einfachsten Anwendungsfall als benannte int-werte Konstanten. Und er hat gerade auf der Mailingliste zum ersten Mal versprochen, dass eine verbesserte Unterstützung am Horizont ist.

Aber ich recentlyused sie eine Liste von Zeichenfolgen zu ersetzen, da die Menge der Werte ein wenig gesetzt ist, schöner als eine Reihe von Zeichenfolgen für die Suche.

Statt

val stuff = List("foo", "bar", "baz") 

object stuff extends Enumeration { val foo, bar, baz = Value } 

oder

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 
             ^

Verhalten:

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 ist ein Bit gesetzt:

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) 

Andere Sachen, die in der heutigen Scala zu funktionieren scheint:

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 

Es sieht aus wie Scala ist 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 
Verwandte Themen