在变量中将变量标记为final是否有意义?

Ska*_*rab 15 optimization groovy final

我想知道标记为final的变量是如何被Groovy解释的(在1.8.0,1.8.1中).我知道它在Java中是有意义的,它可以提高性能 - 当然 - 有助于避免愚蠢的错误.我想学习final是否可以帮助java编译器优化用Groovy编写的程序.我想知道Groovy变换器是否保留变量的最终标记.

Jus*_*per 17

似乎没有groovyc像javac那样内联最终变量.我创建了两个测试脚本,一个使用final,一个不使用:

final String message = "Hello World"
println message
Run Code Online (Sandbox Code Playgroud)
String message = "Hello World"
println message
Run Code Online (Sandbox Code Playgroud)

javap -c 为这两个类生成相同的输出:

   0:   invokestatic    #18; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   ldc #58; //String Hello World
   6:   astore_2
   7:   aload_1
   8:   ldc #59; //int 1
   10:  aaload
   11:  aload_0
   12:  aload_2
   13:  invokeinterface #63,  3; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
   18:  areturn
   19:  nop

javac优化了astore/ aload:

没有final:

   0:   ldc #2; //String Hello World
   2:   astore_1
   3:   getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   aload_1
   7:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   10:  return

final:

   0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc #3; //String Hello World
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

所有这一切,如果性能至关重要,Groovy开始时是一个糟糕的选择.内联最终变量不会为您节省使用反射进行方法调用的开销.


Dón*_*nal 13

正如Justin所说,如果编译器对最终变量执行的优化对您很重要,那么您不应该使用Groovy.

但是,如果Groovy的性能足够好,那么将变量标记为final仍然有用,原因有两个:

  • 保护类的不变量,即确保在构造对象后不能更改值.Java在编译时强制执行此操作,Groovy仅在运行时强制执行此操作,但这比静默允许更改不可变值更好

  • 文档.您班级的用户可以轻松查看允许更改的值

  • 当前版本的Groovy(1.8.2)并不关心final,也没有强制执行任何操作. (3认同)
  • 最近我听到很多关于 Groovy 如何开始提高性能的消息。上面的内容是否仍然反映了 Groovy 2.2 或路线图上版本的情况? (2认同)