Scala中的元注释和私有[this]

时间:2015-06-12 10:07:50

标签: scala

我在Scala中的meta-annotations遇到了一些麻烦。假设有一个类

private class MyClass(@(volatile @field) private[this] var someField: Int) {}

我希望将someField构造函数参数转换为private[this] volatile字段。但是我收到了一个警告:

no valid targets for annotation on variable someField - it is discarded unused. You may specify targets with meta-annotations, e.g. @(volatile @scala.annotation.meta.field @param)

但是,如果我用private[this]替换private,一切都很好。

我使用Scala 2.11.6。

你能帮帮我吗?

2 个答案:

答案 0 :(得分:1)

通过使用javap,您可以看到您的类没有名为someField的字段。原因是你没有使用它,似乎在私有[this]变量的情况下,如果不使用,scala会自动丢弃整个字段。

Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_2
0).
Type in expressions to have them evaluated.
Type :help for more information.

scala>  import scala.annotation.meta._
import scala.annotation.meta._

scala> class MyClass(@(volatile @field) private[this] var someField: Int) {}
<console>:10: warning: no valid targets for annotation on variable someField - i
t is discarded unused. You may specify targets with meta-annotations, e.g. @(vol
atile @scala.annotation.meta.field @param)
       class MyClass(@(volatile @field) private[this] var someField: Int) {}
                      ^
defined class MyClass

scala> :javap -verbose -private MyClass
  Size 562 bytes
  MD5 checksum 05ce62de304576552a958ee79ec4b2b7
  Compiled from "<console>"
public class MyClass
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               MyClass
   #2 = Class              #1             // MyClass
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <console>
   #6 = Utf8               <init>
   #7 = Utf8               (I)V
   #8 = Utf8               ()V
   #9 = NameAndType        #6:#8          // "<init>":()V
  #10 = Methodref          #4.#9          // java/lang/Object."<init>":()V
  #11 = Utf8               this
  #12 = Utf8               LMyClass;
  #13 = Utf8               someField
  #14 = Utf8               I
  #15 = Utf8
  #16 = Class              #15            //
  #17 = Utf8               $line4/$read
  #18 = Class              #17            // $line4/$read
  #19 = Utf8
  #20 = Utf8
  #21 = Class              #20            //
  #22 = Utf8
  #23 = Class              #22            //
  #24 = Utf8
  #25 = Class              #24            //
  #26 = Utf8               MyClass
  #27 = Utf8               Code
  #28 = Utf8               LocalVariableTable
  #29 = Utf8               LineNumberTable
  #30 = Utf8               SourceFile
  #31 = Utf8               InnerClasses
  #32 = Utf8               Scala
{
  public MyClass(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>
":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMyClass;
            0       5     1 someField   I
      LineNumberTable:
        line 14: 0
}
SourceFile: "<console>"
InnerClasses:
     public static #19= #16 of #18; //=class  of class $line4/$read
     public static #19= #21 of #16; //=class  of class
     public static #19= #23 of #21; //=class  of class
     public static #19= #25 of #23; //=class  of class
     public static #26= #2 of #25; //MyClass=class MyClass of class
Error: unknown attribute
  Scala: length = 0x0

但是如果你在你的班级中使用那个变量,你会发现你有一个易变的字段:

Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_2
0).
Type in expressions to have them evaluated.
Type :help for more information.

scala>  import scala.annotation.meta._
import scala.annotation.meta._

scala> class MyClass(@(volatile @field) private[this] var someField: Int) {def u
seTheField=print(someField)}
<console>:10: warning: no valid targets for annotation on variable someField - i
t is discarded unused. You may specify targets with meta-annotations, e.g. @(vol
atile @scala.annotation.meta.field @param)
       class MyClass(@(volatile @field) private[this] var someField: Int) {def u
seTheField=print(someField)}
                      ^
defined class MyClass

scala> :javap -verbose -private MyClass
  Size 854 bytes
  MD5 checksum c7e8340b43d0416e81554b586d122a01
  Compiled from "<console>"
public class MyClass
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               MyClass
   #2 = Class              #1             // MyClass
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <console>
   #6 = Utf8               someField
   #7 = Utf8               I
   #8 = Utf8               useTheField
   #9 = Utf8               ()V
  #10 = Utf8               scala/Predef$
  #11 = Class              #10            // scala/Predef$
  #12 = Utf8               MODULE$
  #13 = Utf8               Lscala/Predef$;
  #14 = NameAndType        #12:#13        // MODULE$:Lscala/Predef$;
  #15 = Fieldref           #11.#14        // scala/Predef$.MODULE$:Lscala/Predef
$;
  #16 = NameAndType        #6:#7          // someField:I
  #17 = Fieldref           #2.#16         // MyClass.someField:I
  #18 = Utf8               scala/runtime/BoxesRunTime
  #19 = Class              #18            // scala/runtime/BoxesRunTime
  #20 = Utf8               boxToInteger
  #21 = Utf8               (I)Ljava/lang/Integer;
  #22 = NameAndType        #20:#21        // boxToInteger:(I)Ljava/lang/Integer;

  #23 = Methodref          #19.#22        // scala/runtime/BoxesRunTime.boxToInt
eger:(I)Ljava/lang/Integer;
  #24 = Utf8               print
  #25 = Utf8               (Ljava/lang/Object;)V
  #26 = NameAndType        #24:#25        // print:(Ljava/lang/Object;)V
  #27 = Methodref          #11.#26        // scala/Predef$.print:(Ljava/lang/Obj
ect;)V
  #28 = Utf8               this
  #29 = Utf8               LMyClass;
  #30 = Utf8               <init>
  #31 = Utf8               (I)V
  #32 = NameAndType        #30:#9         // "<init>":()V
  #33 = Methodref          #4.#32         // java/lang/Object."<init>":()V
  #34 = Utf8
  #35 = Class              #34            //
  #36 = Utf8               $line4/$read
  #37 = Class              #36            // $line4/$read
  #38 = Utf8
  #39 = Utf8
  #40 = Class              #39            //
  #41 = Utf8
  #42 = Class              #41            //
  #43 = Utf8
  #44 = Class              #43            //
  #45 = Utf8               MyClass
  #46 = Utf8               Code
  #47 = Utf8               LocalVariableTable
  #48 = Utf8               LineNumberTable
  #49 = Utf8               SourceFile
  #50 = Utf8               InnerClasses
  #51 = Utf8               Scala
{
  private volatile int someField;
    descriptor: I
    flags: ACC_PRIVATE, ACC_VOLATILE

  public void useTheField();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #15                 // Field scala/Predef$.MODULE$:Lsc
ala/Predef$;
         3: aload_0
         4: getfield      #17                 // Field someField:I
         7: invokestatic  #23                 // Method scala/runtime/BoxesRunTi
me.boxToInteger:(I)Ljava/lang/Integer;
        10: invokevirtual #27                 // Method scala/Predef$.print:(Lja
va/lang/Object;)V
        13: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  this   LMyClass;
      LineNumberTable:
        line 10: 0

  public MyClass(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #17                 // Field someField:I
         5: aload_0
         6: invokespecial #33                 // Method java/lang/Object."<init>
":()V
         9: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   LMyClass;
            0      10     1 someField   I
      LineNumberTable:
        line 10: 0
        line 14: 5
}
SourceFile: "<console>"
InnerClasses:
     public static #38= #35 of #37; //=class  of class $line4/$read
     public static #38= #40 of #35; //=class  of class
     public static #38= #42 of #40; //=class  of class
     public static #38= #44 of #42; //=class  of class
     public static #45= #2 of #44; //MyClass=class MyClass of class
Error: unknown attribute
  Scala: length = 0x0

唯一的问题是你仍然会收到不使用volatile注释的警告,尽管它在编译的代码中肯定是正确使用的。因此,对于第二种情况,我认为显示警告是一个错误是安全的。

答案 1 :(得分:0)

这里没有专家,但以下似乎有效:

import annotation.meta.field

class Foo(init: Int) {
  @volatile @field private[this] var x: Int = init
}

阅读(非常混乱)documentation,我感觉您的代码存在以下问题:@(volatile @field)表示将volatile注释复制到字段<{em>} someField而不是默认目标构造函数参数。 但是:您没有指定someField本身应该成为一个字段,因此没有目标。不确定这是否有意义。