2011-10-02 12 views
25

In Java, quando si utilizza un oggetto su più thread (e in generale), è buona pratica rendere i campi definitivi. Ad esempio,Scala finale vs val per visibilità concorrenza

public class ShareMe { 
    private final MyObject obj; 
    public ShareMe(MyObject obj) { 
     this.obj = obj; 
    } 
} 

In questo caso, la visibilità di obj sarà coerente su più thread (supponiamo obj ha tutti i campi finali pure) in quanto è costruito in modo sicuro utilizzando la parola chiave finale.

In scala, non viene visualizzato il valore di val in un riferimento finale, ma piuttosto val è la semantica in scala che impedisce di riassegnare una variabile (Scala final variables in constructor). Se le variabili del costruttore di scala non sono definite come definitive, soffriranno dello stesso problema (quando si usano questi oggetti negli attori)?

risposta

44

La risposta alla altra questione è fuorviante. Ci sono due significati del termine final: a) per campi/metodi Scala e metodi Java significa "non può essere sovrascritto in una sottoclasse" eb) per i campi Java e nel bytecode JVM significa "il campo deve essere inizializzato nel costruttore e non può essere riassegnato ".

I parametri di classe contrassegnati con val (o, equivalentemente, i parametri della classe del caso senza un modificatore) sono effettivamente definitivi nel secondo senso e quindi thread-safe.

Ecco la prova:

scala> class A(val a: Any); class B(final val b: Any); class C(var c: Any) 
defined class A 
defined class B 
defined class C 

scala> import java.lang.reflect._ 
import java.lang.reflect._ 

scala> def isFinal(cls: Class[_], fieldName: String) = { 
    | val f = cls.getDeclaredFields.find(_.getName == fieldName).get 
    | val mods = f.getModifiers 
    | Modifier.isFinal(mods) 
    | } 
isFinal: (cls: Class[_], fieldName: String)Boolean 

scala> isFinal(classOf[A], "a") 
res32: Boolean = true 

scala> isFinal(classOf[B], "b") 
res33: Boolean = true 

scala> isFinal(classOf[C], "c") 
res34: Boolean = false 

O con javap, che può essere eseguito comodamente dal REPL:

scala> class A(val a: Any) 
defined class A 

scala> :javap -private A 
Compiled from "<console>" 
public class A extends java.lang.Object implements scala.ScalaObject{ 
    private final java.lang.Object a; 
    public java.lang.Object a(); 
    public A(java.lang.Object); 
} 
+0

grazie per questo chiarimento. Riesci a vedere il mio aggiornamento a questa domanda? Sto anche vedendo le variabili del costruttore dichiarate come finali (come mostrato in javap) per una classe non case senza alcun modificatore. –

+0

@retronym, sono giunto alla tua risposta cercando di trovare una risposta alla mia domanda, [Immutability e thread-safety in Scala] (http://stackoverflow.com/questions/32113124/immutability-and-thread-safety-in- Scala). Come sapete, la parola chiave 'final' in Java viene utilizzata anche per evitare il trasferimento delle istruzioni del ctor, come indicato in [questo] (https://www.cs.umd.edu/~pugh/java/memoryModel /jsr-133-faq.html#finalRight) link. Quindi, se ho capito bene la tua risposta, usare un campo 'val' in una classe di Scala equivale a dichiarare un campo' finale' in una classe Java, lasciando che il JMM funzioni correttamente? –

1

Penso di aver frainteso il modo in cui var è compilato. Ho creato la classe del campione

class AVarTest(name:String) { 
    def printName() { 
    println(name) 
    } 
} 

mi sono imbattuto javap -private e si è tradotto in

public class AVarTest extends java.lang.Object implements scala.ScalaObject{ 
    private final java.lang.String name; 
    public void printName(); 
    public AVarTest(java.lang.String); 
} 

E il nome è in realtà compilato alla finale.

Ciò è dimostrato anche in Scala val has to be guarded with synchronized for concurrent access?

+2

Nota che a partire da 2.9.0.1 (forse prima) l'unica ragione per cui si finisce con un nome di campo è perché l'hai fatto riferimento in printName. Se nessun metodo fa riferimento a un argomento di attore, non genera a lungo un campo. –