2011-10-02 18 views
25

En Java, cuando se usa un objeto en varios hilos (y en general), es una buena práctica hacer campos finales. Por ejemplo,Scala final vs val para visibilidad de concurrencia

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

En este caso, la visibilidad de obj será consistente a través de múltiples hilos (Asumamos obj tiene todos los campos finales así), ya que se construye de manera segura usando la palabra clave final.

En scala, no parece val compila hasta una referencia final, sino val es semántica en scala que evita que reasigne una variable (Scala final variables in constructor). Si las variables del constructor scala no se definen como finales, ¿sufrirán el mismo problema (al usar estos objetos en los actores)?

Respuesta

44

La respuesta a la otra pregunta es engañosa. Hay dos significados del término final: a) para los campos/métodos Scala y los métodos Java significa que "no se puede sobrescribir en una subclase" yb) para los campos Java y en bytecode JVM significa que "el campo debe inicializarse en el constructor" y no puede ser reasignado ".

Los parámetros de clase marcados con val (o, de forma equivalente, los parámetros de clase de caso sin un modificador) son definitivamente finales en segundo sentido, y por lo tanto son seguros para subprocesos.

Aquí está la prueba:

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, que se puede ejecutar cómodamente desde el 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

gracias por esa aclaración. ¿Puedes ver mi actualización a esa pregunta? También veo las variables de constructor declaradas como finales (como se muestra en javap) para una clase que no sea de caso sin modificador. –

+0

@retronym, llegué a su respuesta tratando de encontrar una respuesta a mi pregunta, [Inmutabilidad y seguridad de hilos en Scala] (http://stackoverflow.com/questions/32113124/immutability-and-thread-safety-in- scala). Como sabe, la palabra clave 'final' en Java también se utiliza para evitar la reubicación de las instrucciones del ctor, como se indica en [esto] (https://www.cs.umd.edu/~pugh/java/memoryModel /jsr-133-faq.html#finalRight) enlace. Entonces, si he entendido bien tu respuesta, usar un campo 'val' en una clase Scala equivale a declarar un campo' final' en una clase Java, ¿cómo funciona el JMM correctamente? –

1

Creo que he entendido mal cómo se compila var. He creado la clase de muestra

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

me encontré javap -private y resultó en

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

Y el nombre es en realidad compilado al final.

Esto también se muestra en la Scala val has to be guarded with synchronized for concurrent access?

+2

nota de que al 2.9.0.1 (quizás antes) la única razón por la que se termina con un nombre de campo es porque lo hizo referencia en printName. Si no hay ningún método que haga referencia a un argumento ctor, no genera un campo. –

Cuestiones relacionadas