简体   繁体   中英

Use annotation processor generated constants indrectly in annotation will lead compile error

Get minimize code to reproduce this

I have a HelloWorldProcessor who will simply generate source file HelloWorldMessage.java

public interface HelloWorldMessage { 
  String HELLO_WORLD = "Hello World";
}

Now I use the generated value in my code:

public class UseHelloWorld {
  @Anno(HelloWorldMessage.HELLO_WORLD)
  public void func(){
  }
}

That works fine.

But if I declared the value as constant and use it indirectly, it will cause compile error.

public class UseHelloWorld{
  public static final String HW =  HelloWorldMessage.HELLO_WORLD;

  @Anno(UseHelloWorld.HW)
  public void func(){
  }
}

Javac gives symbol not found error:

UseHelloWorld.java:2: error: cannot find symbol
  public static final String HW = HelloWorldMessage.HELLO_WORLD;
                                  ^
  symbol:   variable HelloWorldMessage
  location: class UseHelloWorld
UseHelloWorld.java:4: error: element value must be a constant expression
  @Anno(UseHelloWorld.HW)
                     ^
2 errors

Why I said 'javac' is because it works fine in eclipse with ECJ and m2e-apt.

Is this a javac bug? If no, how can I correct use the generated source indirectly?

I didn't know this subject so tried to play with it a little bit. It seems that your example will not work with javac , by following the steps you provide, I don't think the processor is called. You could try with some debug parameters to verity it:

  1. -XprintProcessorInfo : Print information about which annotations a processor is asked to process
  2. -XprintRounds : Print information about initial and subsequent annotation processing rounds.
  3. -verbose :Verbose output. This includes information about each class loaded and each source file compiled.

Secondly, it seems like you'll need to provide the processor as a jar, with /META-INF/services/javax.annotation.processing.Processor in it. I've tried this, it was better but still failed, I think it's because the source file doesn't compile, so the compiler can not retrieve any information about the annotation.(I thought it could by just scanning the source file).

Finally I made it work by moving the use of annotation from UseHelloWorld to a package-info.java file(or any other files which compile). now the compiler can see that the annotation has been used in a source file, and the processor gets called to generate the HelloWorldMessage class which is compiled in the following round. And the class UseHelloWorld also compiles.

Note: I've added some import and package in your files, otherwise it doesn't compile even with the presence of HelloWorldMessage class.

I think why it works in Eclipse is because different tools are being used to do the processing. Or maybe you've generated the source file some time and forget to clean it. Hope someone can give a better answer, I'm just sharing my experiments.


Update

I was stupid by saying we should provide the processor as a jar, obviously a class file is enough (I may have used a wrong classpath). My guess to the updated post is the compiler doesn't handle every case. In Eclipse, we see two different errors concerning @Anno

  1. with @Anno(HelloWorldMessage.HELLO_WORLD) : HelloWorldMessage cannot be resolved to a variable
  2. with @Anno(UseHelloWorld.HW) : The value for annotation attribute Anno.value must be a constant expression

Maybe in the first case the compiler is clever enough to guess the unknown type can be generated by the annotation processor, so it gives a try, and in the second case it treats it as the annotation is incorrectly used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM