2012-09-06 8 views
26

¿Cuál es el motivo de que val s no (?) Sea automáticamente final en objetos singleton? P.ej.¿Por qué un `val` dentro de un` objeto` no es automáticamente final?

object NonFinal { 
    val a = 0 
    val b = 1 

    def test(i: Int) = (i: @annotation.switch) match { 
     case `a` => true 
     case `b` => false 
    } 
} 

resultados en:

<console>:12: error: could not emit switch for @switch annotated match 
      def test(i: Int) = (i: @annotation.switch) match { 
                ^

Mientras

object Final { 
    final val a = 0 
    final val b = 1 

    def test(i: Int) = (i: @annotation.switch) match { 
     case `a` => true 
     case `b` => false 
    } 
} 

compila sin advertencias, por lo que presumiblemente genera la tabla de comparación de patrones más rápido.

Tener que agregar final me parece un ruido molesto. ¿No es una final object per se, y por lo tanto también sus miembros?

+0

Hm. ¿Qué hay de los rasgos que podrían usarse? –

+0

@TonyK. - ¿Qué quieres decir? Incluso si tuviera 'rasgo T {def a: Int}', el objeto anularía 'a' en las reglas de linealización. Si tuviera 'rasgo T {def a: Int = 33}', ok en ese caso 'anular valor final' no es posible. Pero creo que eso aún no descalifica el enfoque de hacer que _non-overriding_ vals final sea el predeterminado. –

+0

de acuerdo. Soy relativamente nuevo en Scala ... estaba intentando explorar posibles avenidas en las que no habías pensado. –

Respuesta

20

Esto se aborda explícitamente en the specification, y son automáticamente definitiva:

Los miembros de clases finales o los objetos son también implícitamente final, por lo el modificador final es generalmente redundante para ellos, también. Sin embargo, tenga en cuenta que las definiciones de valores constantes (§4.1) requieren un modificador explícito final, incluso si se definen en una clase u objeto final.

Su final ejemplo -menos compila sin errores (o advertencias) con 2,10-M7, así que asumiría que hay un problema con el @switch comprobar en las versiones anteriores, y que los miembros están en último hecho.


Actualización: En realidad esto es más curioso de lo que esperaba, si compilamos el siguiente, ya sea con o 2.9.2 2.10-M7:

object NonFinal { 
    val a = 0 
} 

object Final { 
    final val a = 0 
} 

javap es suficiente para demostrar una diferencia:

public final class NonFinal$ implements scala.ScalaObject { 
    public static final NonFinal$ MODULE$; 
    public static {}; 
    public int a(); 
} 

public final class Final$ implements scala.ScalaObject { 
    public static final Final$ MODULE$; 
    public static {}; 
    public final int a(); 
} 

Ve lo mismo, incluso si el lado derecho de las definiciones de valores no es una expresión constante.

Así que voy a dejar mi respuesta, pero no es concluyente.

+2

Pensé que solo podía activar valores constantes, en cuyo caso 2.10-M7 eliminó el requisito de 'final' que citó. ¿No? –

+0

@RexKerr: De hecho. No recuerdo haber visto eso en ninguno de los registros de cambios, pero volveré a verificar. –

+1

Gracias, esa cita de la especificación es bastante clara, diciendo 'val's necesita explícita' final'. El 2.10.0-M7 es extraño entonces, podría valer la pena verificar el código de coincidencia de patrón resultante (¿quizás se haya omitido silenciosamente '@ switch?) –

1

Para abordar la cuestión central sobre el final sobre un objeto, creo que esta cláusula de la especificación es más relevante:

una definición de valor constante es de la forma final val x = e donde e es una expresión constante (§6.24). El modificador final debe estar presente y no se puede dar ninguna anotación de tipo. Las referencias al valor constante x se tratan a sí mismas como expresiones constantes; en el código generado son reemplazados por el lado derecho de la definición e.

De importancia:

  • Sin tipo de anotación se puede dar
  • La expresión E se utiliza en el código generado (por mi lectura, ya que la expresión de la constante sin evaluar el original)

Me parece que la especificación requiere que el compilador use estas macrocadenas más que los valores que se evalúan en el momento de la compilación, lo que podría tener un impacto en cómo la co resultante de carreras.

Creo que es particularmente interesante que no se pueda dar ninguna anotación de tipo.

Esto, creo que apunta a nuestra respuesta final, aunque no puedo encontrar un ejemplo que muestre la diferencia de tiempo de ejecución para estos requisitos. De hecho, en mi intérprete 2.9.2, ni siquiera recibo la aplicación de la primera regla.

20

No pregunta "¿por qué no son definitivas?", Se pregunta "¿por qué no están en línea?" Simplemente sucede que final es la forma en que se indica el compilador que quiere que estén en línea.

La razón por la que no están automáticamente en línea es la compilación por separado.

object A { final val x = 55 } 
object B { def f = A.x } 

Al compilar esto, vuelve B.f 55, literalmente:

public int f(); 
    0: bipush  55 
    2: ireturn  

Eso significa que si vuelve a compilar A, B será ajeno al cambio. Si x no está marcado final en A, B.f se parece a esto en su lugar:

0: getstatic  #19     // Field A$.MODULE$:LA$; 
    3: invokevirtual #22     // Method A$.x:()I 
    6: ireturn  

Además, para corregir una de las otras respuestas, última no significa inmutable en Scala.

+1

final no significa constante tampoco. Pruebe con "final var x = 1; x = 2". – extempore

+0

parece que esta debería ser la respuesta aceptada – fredoverflow

Cuestiones relacionadas