2014-08-29 16 views
5

Ho letto an answer on SO where someone said that scala enumerations are useless e dovresti semplicemente utilizzare le enumerazioni java, se necessario.Differenze tra le enumerazioni scala e java

Anche se ho già utilizzato le enumerazioni java, non posso dire di capirle completamente perché non codifico in java durante il mio lavoro diurno.

Qualcuno può spiegare le differenze tra le enumerazioni scala e java e dove sono esattamente le carenze in scala enum?

+0

di Scala non è che dietro. http://stackoverflow.com/questions/21537148/scala-java-enumerations Non è conveniente quanto le classi/oggetti case di Scala. – Naetmul

risposta

8

La principale debolezza delle enumerazioni di Scala è la difficoltà di aggiungere metodi e campi. In Java, è banale usare un enum come classe con un numero fisso di istanze (che è, ad esempio, il motivo per cui sono una buona scelta per implementare i singleton).

In Scala, invece, le Enum sono solo un insieme di valori possibili; aggiungere metodi e campi a questi è un processo molto più hacky. Quindi, se vuoi qualcosa che va oltre l'insignificante comportamento dell'enum, le enumerazioni Java sono uno strumento molto più conveniente.

Ad esempio, un enum Java potrebbe assomigliare a questo:

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

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

Tuttavia, a Scala, si dovrebbe fare questo:

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

Questo non sembra così male in un banale esempio come questo, ma aggiunge una sintassi confusa e aggiunge l'overhead di un'altra classe che dovrà essere convertita implicitamente in molti usi dell'enumerazione.

Il vantaggio principale che vedo sull'enumerazione Java è che si scrive esattamente cosa si intende; un enum, con alcuni metodi e campi su di esso. In scala, stai scrivendo qualcosa di diverso da quello che intendi; è necessario creare un enum, che contiene una classe che ha i metodi e i campi che si desidera e definisce anche una conversione da quella classe all'enumerazione. È un modo meno idiomatico di esprimere la stessa idea.

Come è stato precisato in the comments, Scala offre Case Classes, che può essere utilizzato in alternativa all'enumerazione in molti casi e ha una sintassi molto più pulita. Ma ci sono ancora alcune situazioni in cui le Classi Case non sono sufficienti (come quando si desidera eseguire un'iterazione su tutti i valori), quindi le enumerazioni regolari hanno ancora il loro posto. Correzione: l'uso di macro rende possibile iterare su Classi di casi, ma così facendo ha la sua complessità aggiuntiva, mentre l'enumerazione (sia in Scala che in Java) è molto più semplice da ripetere.

+1

Vale la pena notare che una terza soluzione consiste nell'utilizzare gli oggetti case [come discusso qui] (http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala). –

+0

E l'iterazione su caratteri sigillati è possibile con macro - http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala – Gavin

3

Il vantaggio principale dell'Enumerazione di Scala è la regolarità della sintassi.

Se si desidera aggiungere un comportamento agli elementi dell'enumerazione, è sufficiente estendere la classe Val.

A Odersky piacciono per il loro caso d'uso più semplice come costanti int-valutate con nome. E ha appena promesso sulla mailing list, per la prima volta, che un supporto migliorato è all'orizzonte.

Ma I recentlyused per sostituire un elenco di stringhe, poiché l'insieme di valori è un po 'impostato, più bello di un set di stringhe per la ricerca.

Invece di

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

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

o

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 
             ^

Comportamento:

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 è un insieme di bit:

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) 

Altre cose che sembra funzionare a Scala di oggi:

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 

Sembra Scala è not entirely friendly to Java enums: Enumeration

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