Search code examples
javaannotationsjavac

How to avoid javac warning: unknown enum constant, reason: class file not found, when dependency has optional annotations with enums


I have this example setup:

p.E

package p;

public enum E { E }

p.A

package p;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface A {
    p.E e();
}

q.Test

package q;

import p.*;

@A(e = E.E)
public class Test {
    public static void main(String[] args) {
        System.out.println("Test.main()");
    }
}

So, I can now do this:

$ javac q/Test.java
$ rm p/*.class
$ ll */*
-rw-r--r-- 1 lukas 197609 118 Mar 28 10:21 p/A.java
-rw-r--r-- 1 lukas 197609  32 Mar 28 09:43 p/E.java
-rw-r--r-- 1 lukas 197609 486 Mar 28 10:22 q/Test.class
-rw-r--r-- 1 lukas 197609 153 Mar 28 09:55 q/Test.java
$ java q/Test
Test.main()

No problem, the annotations are optional and ignored by the runtime, no NoClassDefFoundError

However, when I remove the source files of p as well, keeping the compiled Test.class:

$ rm -Rf p

And then create:

x.Test2

package x;

public class Test2 {
    q.Test test;
}

Compiling that class gives a warning:

$ javac x/Test2.java
warning: unknown enum constant E.E
  reason: class file for p.E not found
1 warning

Alternatives, that don't produce such a warning

No enum

I don't get this warning for an annotation that doesn't depend on any enums. I.e. repeat the procedure for these, and there's no warning:

p.A

package p;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface A {}

q.Test

package q;

import p.*;

@A
public class Test {
    public static void main(String[] args) {
        System.out.println("Test.main()");
    }
}

Class reference

When A references Class<E> instead of an E.E enum value, I still don't get any warnings:

p.A

package p;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface A {
    Class<?> a();
}

q.Test

package q;

import p.*;

@A(a = E.class)
public class Test {
    public static void main(String[] args) {
        System.out.println("Test.main()");
    }
}

My questions:

  1. Is this a correct warning according to the JLS? It seems like a bug in javac to me, given the annotation is optional and doesn't produce a warning if there's no enum type involved, but I may be missing something.
  2. Is there any way to work around this warning in client code?

For context: the real world use-case is:

  • The p package containing the annotations are from JAXB
  • The q package containing library code that references the annotations is a library whose dependency on p is optional. (jOOQ to be specific, more details here)
  • The x package is client code, depending on the q package, which has an optional dependency on p, meaning, the dependency is effectively missing by default.

Solution

  • As commented by user yyyy and also on the library bug tracker, the current behaviour is defined by a fix JDK-6550655, which introduced the warning in Java 7 (before, this annotation/enum usage resulted in a compilation error).

    JDK-6550655 is slightly different. The warning was introduced for cases where only the enum p.E is absent, not the annotation p.A, in case of which, indeed, undesired runtime errors will occur when accessing p.A using reflection API. Warning developers of this situation is certainly desired. In this case here, however, all content of the p package is absent, meaning there can't be any runtime errors, and the absence should be fine as per the design of annotations, which are allowed to be absent.

    In my opinion, this is a bug in javac, which I've reported: JDK-8305250. It may or may not be accepted or fixed.