2014-08-29 11 views
5

Czytam an answer on SO where someone said that scala enumerations are useless i powinieneś po prostu używać wyliczeń java, jeśli naprawdę potrzebujesz.Różnice między wyliczeniami scala i java

Mimo że wcześniej używałem wyliczeń java, nie mogę powiedzieć, że w pełni je rozumiem, ponieważ nie koduję w języku Java podczas mojej codziennej pracy.

Czy ktoś może wyjaśnić różnice między wyliczeniami scala i java, a gdzie dokładnie występują niedociągnięcia w scala enum?

+0

Scala nie jest to tyle. http://stackoverflow.com/questions/21537148/scala-java-enumerations To nie jest tak wygodne, jak klasy/obiekty Scala. – Naetmul

Odpowiedz

8

Główną słabością wyliczanek Scala jest trudność w dodawaniu do nich metod i pól. W języku Java, trywialne jest używanie wyliczenia jako klasy z ustaloną liczbą instancji (na przykład, dlaczego są dobrym wyborem do implementacji singletonów).

W Scali jednak wyliczenia to tylko zestaw możliwych wartości; dodawanie do nich metod i pól jest o wiele bardziej zaciekłym procesem. Więc jeśli chcesz czegoś poza banalnym zachowaniem z enum, wyliczenia Java są znacznie wygodniejszym narzędziem.

Na przykład enum Java może wyglądać następująco:

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

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

Jednak w Scala, trzeba by to zrobić:

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

To nie wygląda tak źle w trywialny przykład taki jak ten, ale dodaje trochę niejasnej składni i dodaje narzut innej klasy, która będzie musiała być niejawnie przekonwertowana na większość zastosowań wyliczenia.

Główną zaletą, którą widzę w wyliczeniu Java jest to, że piszesz dokładnie to, co masz na myśli; enum, z niektórymi metodami i dziedzinami. W scala piszesz coś innego niż to, co masz na myśli; musisz utworzyć wyliczenie, które zawiera klasę, która ma odpowiednie metody i pola, a także definiuje konwersję z tej klasy do wyliczenia. To mniej idiotyczny sposób wyrażania tego samego pomysłu.

Jak wskazano, in the comments, Scala oferuje Case Classes, która w wielu przypadkach może być używana jako alternatywa do wyliczeń i ma znacznie czystszą składnię. Ale nadal istnieją sytuacje, w których klasy przypadków są niewystarczające (np. Gdy chcesz powtórzyć wszystkie wartości), więc regularne wyliczenia wciąż mają swoje miejsce. Korekta: używanie makr pozwala na powtarzanie klas przypadków, ale robi to z własną złożonością, podczas gdy wyliczenia (zarówno w Scali, jak i Java) są znacznie prostsze do iterowania.

+1

Warto zauważyć, że trzecim rozwiązaniem jest użycie obiektów przypadku, [jak omówiono tutaj] (http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala). –

+0

I iteracja nad zamkniętą cechą jest możliwa z makrami - http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala – Gavin

3

Główną zaletą wyliczenia Scali jest prawidłowość składni.

Jeśli chcesz dodać zachowanie do elementów swojego wyliczenia, po prostu rozszerz jego klasę Val.

Odersky lubi je dla ich najprostszego przypadku użycia jako nazwane stałe z wartościami stałymi. I właśnie obiecał na liście mailingowej, po raz pierwszy, że ulepszone wsparcie jest na horyzoncie.

Ale I recentlyused je zastąpić listę ciągów, ponieważ zestaw wartości jest nieco zestaw, ładniejszy niż zestaw ciągów do wyszukiwania.

Zamiast

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

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

lub

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 
             ^

Zachowanie:

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 to zestaw bitów:

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) 

Inne rzeczy, które wydaje się działać w dzisiejszym 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 

Wygląda Scala jest not entirely friendly to Java enums: Wyliczanie

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 
Powiązane problemy