[JAVA] Unclear error encountered in annotation processing * Unresolved

I ran into an error I didn't understand when I made my own annotation process. Make a note of what kind of error you know.

environment

Java

openjdk 10.0.2

Gradle

Gradle 4.10

Eclipse

4.8.0 (Photon)Pleiades

Implementation

Folder structure


|-settings.gradle
|-build.gradle
|-foo/
| `-src/main/java/
|   `-foo/
|     |-Foo.java
|     |-ConcreteClass.java
|     |-MyAnnotation.java
|     `-MyInterface.java
|
`-bar/
  `src/main/
   |-java/
   | `-bar/
   |   `-MyProcessor.java
   `-resources/
     `-META-INF/services/
       `-javax.annotation.processing.Processor

--Multi-project of foo, bar --bar implements annotation processing and foo uses it

MyProcessor.java


package bar;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;

public class MyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (!annotations.isEmpty()) {
            try {
                JavaFileObject javaFileObject = this.processingEnv.getFiler().createSourceFile("foo.AutoGeneratedClass");
                try (BufferedWriter writer = new BufferedWriter(javaFileObject.openWriter())) {
                    writer.write(
                        "package foo;\n" +
                        "import foo.MyInterface;\n" +
                        "public class AutoGeneratedClass implements MyInterface {}"
                    );
                }
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
        
        return true;
    }
    
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latest();
    }
    
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of("foo.MyAnnotation");
    }
}

--Implemented annotation processing for MyAnnotation annotations --A class called ʻAutoGeneratedClass that implements the MyInterface` interface is automatically generated.

MyInterface.java


package foo;

public interface MyInterface {}

--Just an interface

MyAnnotation.java


package foo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    Class<? extends MyInterface> clazz();
}

--Annotation to be processed --The clazz element accepts Class instances up to MyInterface

ConcreteClass.java


package foo;

public class ConcreteClass extends AutoGeneratedClass {}

--Create ConcreteClass by inheriting ʻAutoGeneratedClass` automatically generated by annotation processing

Foo.java


package foo;

@MyAnnotation(clazz=ConcreteClass.class)
public class Foo {}

--Class that actually uses MyAnnotation --The clazz element has a Class instance of ConcreteClass.

Organize with class diagram

apt.jpg

Events that occur

Compiling this looks like this:

> gradle :foo:compileJava
> Task :foo:compileJava FAILED
...\foo\src\main\java\foo\ConcreteClass.java:3:error:Can't find symbol
public class ConcreteClass extends AutoGeneratedClass {
                                   ^
symbol:Class AutoGeneratedClass
...\foo\src\main\java\foo\Foo.java:3:error:Incompatible type: Class<ConcreteClass>Class<? extends MyInterface>Cannot be converted to:
@MyAnnotation(clazz=ConcreteClass.class)
                                 ^
2 errors

--Failed to compile ConcreteClass because ʻAutoGeneratedClass`, which should be automatically generated, cannot be found.

Try various things

Does not occur in Eclipse

--If you import this project into Eclipse as it is and perform annotation processing, no error will occur for some reason. --I tried compiling with only the plain javac command without using Gradle, but an error still occurred.

It will not occur if you stop specifying the upper limit of the type argument

MyAnnotation.java


package foo;

...

public @interface MyAnnotation {
-   Class<? extends MyInterface> clazz();
+   Class<?> clazz();
}

--Remove the upper limit of MyInterface specified in the clazz element of MyAnnotation

Compile result


> gradle :foo:compileJava
...
BUILD SUCCESSFUL in 3s

--Then, the compilation will pass. --Mystery

No error if the upper limit is set to an automatically generated class

MyAnnotation.java


package foo;

...
public @interface MyAnnotation {
-   Class<? extends MyInterface> clazz();
+   Class<? extends AutoGeneratedClass> clazz();
}

--Change the upper limit of clazz from MyInterface to the auto-generated class ʻAutoGeneratedClass`

Compile result


> gradle :foo:compileJava
...
BUILD SUCCESSFUL in 3s

--Compile will pass --Mystery

No error occurs even if the automatically generated class is specified by clazz

Foo.java


package foo;

- @MyAnnotation(clazz=ConcreteClass.class)
+ @MyAnnotation(clazz=AutoGeneratedClass.class)
public class Foo {}

--MyAnnotation has the upper limit ofClass <? Extends MyInterface>

Compile result


...
BUILD SUCCESSFUL in 4s

--This also compiles --Interface-> Auto-generated class-> It seems that an error occurs when it becomes a relationship of self-made class

Depending on ConcreteClass is fine

Foo.java


package foo;

- @MyAnnotation(clazz=ConcreteClass.class)
+ @MyAnnotation
public class Foo {
+   private Class<?> clazz = ConcreteClass.class;
}

--Removed the clazz element of MyAnnotation --Use ConcreteClass in the field --See also ConcreteClass.class

Compile result


> gradle :foo:compileJava
BUILD SUCCESSFUL in 3s

--Compile passes --It seems that there is no problem in depending on the subclass of the automatically generated class.

It is useless to refer to it with annotations that are not processed

OtherAnnotation.java


package foo;

...

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface OtherAnnotation {
    Class<? extends MyInterface> clazz();
}

--Create ʻOtherAnnotation.java with the same definition as MyAnnotation`

Foo.java


package foo;

- @MyAnnotation(clazz=ConcreteClass.class)
+ @MyAnnotation
+ @OtherAnnotation(clazz=ConcreteClass.class)
public class Foo {}

--Added ʻOtherAnnotation to Foo --Theclazz element towards MyAnnotation` has been removed

Compile result


> gradle :foo:compileJava
> Task :foo:compileJava FAILED
...\foo\src\main\java\foo\ConcreteClass.java:3:error:Can't find symbol
public class ConcreteClass extends AutoGeneratedClass {
                                   ^
symbol:Class AutoGeneratedClass
...\foo\src\main\java\foo\Foo.java:4:error:Incompatible type: Class<ConcreteClass>Class<? extends MyInterface>Cannot be converted to:
@OtherAnnotation(clazz=ConcreteClass.class)
                                    ^
2 errors

--The same error as for MyAnnotation --It seems that it doesn't matter whether the annotation is the target of annotation processing.

You can't write it in a place that has nothing to do with annotation processing

OtherClass.java


package foo;

@OtherAnnotation(clazz=ConcreteClass.class)
public class OtherClass {}

Foo.java


package foo;

- @MyAnnotation(clazz=ConcreteClass.class)
+ @MyAnnotation
public class Foo {}

--ʻOtherAnnotationis the same as the previous one --You are using@OtherAnnotation (clazz = ConcreteClass.class) in ʻOtherClass, which has nothing to do with annotation processing.

Compile result


> Task :foo:compileJava FAILED
...\foo\src\main\java\foo\ConcreteClass.java:3:error:Can't find symbol
public class ConcreteClass extends AutoGeneratedClass {
                                   ^
symbol:Class AutoGeneratedClass
...\foo\src\main\java\foo\OtherClass.java:3:error:Incompatible type: Class<ConcreteClass>Class<? extends MyInterface>Cannot be converted to:
@OtherAnnotation(clazz=ConcreteClass.class)
                                    ^
2 errors

--It seems that an error will occur if ConcreteClass.class is referenced by an annotation element even if it is not a class that is directly subject to annotation processing.

Organize symptoms

If you organize the results of various trials, it seems that an error will occur when the following conditions are met?

--The Class instance is specified in the annotation element. --The Class instance is a" self-made class "in the hierarchical relationship of" self-made interface-> class automatically generated by annotation processing-> self-made class ". --The definition of the element that receives the Class instance on the annotation side isClass <? Extends self-made interface>. --Executed by OpenJDK annotation processing

Conclusion

I'm not sure.

Recommended Posts

Unclear error encountered in annotation processing * Unresolved
Error encountered in tagging function implementation
Use MouseListener in Processing
Error handling in Graphql-ruby