Why Is Javac Failing on @Override Annotation

Why is javac failing on @Override annotation

The @Override annotation spec changed in Java 1.6. In Java 1.5, the compiler did not allow the @Override annotation on implemented interface methods, but in 1.6 it does. First search result I found is a blog post here.. It was not well documented, but it did change.

Eclipse is adding it because your Eclipse is set for 1.6 compliance. You should try to keep your build and eclipse environments on the same version of Java. It's unclear to me by your specifying Cruise Control is running Java 5 on whether or not it is compiling using a separate JDK6 or not.

Separate from the above 1.5 vs 1.6 @Override annotation rules, remember that Eclipse has its own compiler implementation (not javac) and will occasionally have different behavior. Whenever something compiles in Eclipse, but not Ant or Maven, you will need to find a way to make both compilers happy.

Here's a screenshot of changing the compiler in eclipse

Why is javac -source 1.5 allowing @Override on interface methods?

Looking at Oracle's documentation of javac for Java SE8 it says:

...

-source release

Specifies the version of source code accepted. The following values for release are allowed:

...

  • 1.5

    The compiler accepts code containing generics and other language features introduced in Java SE 5.

  • 5

    Synonym for 1.5.

  • 1.6

    No language changes were introduced in Java SE 6. However, encoding errors in source files are now reported as errors instead of warnings as in earlier releases of Java Platform, Standard Edition.

...

Notice that the site says that "no language changes were introduced in Java SE 6". Thus, the fact that @Override could be used to assert that interface-methods were overridden seems to be not regarded as a language change; rather, the fact that it wasn't previously allowed may be regarded as simply a bug in some Java 5 SDKs compilers.

This behaviour is rectified since @Override can be omitted after compilation and the JDK 8 compiler understands (and validates) the annotation. Thus, the annotation has no effect whatsoever on the created bytecode.

With -source 1.4, we get a compilation error since Annotations were introduced with Java 1.5, thus including annotations that are retained at runtime could change the behaviour of the program and the compiler must not ignore them.


If we want to find all incompatibilites with Java SE 5, I recommend downloading Java SE5 JDK from the Oracle Archive and trying to compile the project with this JDK.

Why does Eclipse complain about @Override on interface methods?

Using the @Override annotation on methods that implement those declared by an interface is only valid from Java 6 onward. It's an error in Java 5.

Make sure that your IDE projects are setup to use a Java 6 JRE, and that the "source compatibility" is set to 1.6 or greater:

  1. Open the Window > Preferences dialog
  2. Browse to Java > Compiler.
  3. There, set the "Compiler compliance level" to 1.6.

Remember that Eclipse can override these global settings for a specific project, so check those too.


Update:

The error under Java 5 isn't just with Eclipse; using javac directly from the command line will give you the same error. It is not valid Java 5 source code.

However, you can specify the -target 1.5 option to JDK 6's javac, which will produce a Java 5 version class file from the Java 6 source code.

Remove @Override annotation error in Java8

Please let me know why is the error coming or help me fix it?

This can happen when the latest class files are not being generated i.e., the override method signature is not matching because the class files are still referring to old. So, ensure that the project is built properly and the latest class files are being generated.

In eclipse, Project (at the top bar) -> clean and check whether the latest class files are being generated (in the project/target/classes folder or any other which you have configured). Also, ensure that there are no other errors in the project which might prevent the class files generation. You can also look here on why project can't be built.

Error on @Override annotation with interface implementation

Realized no Builder is selected for current project. Selected Java Builder and boom. No more red flags all over the code.

Sample Image

@Override annotation on implemented method of interface in Java 5 code doesn't give a compilation error

The core of the issue: Maven is still compiling your code with the JDK with which it is launched. Since you're using JDK 8, it is compiling with JDK 8, and to compile with another compiler, you need to use toolchains or specify the path to the right JDK.

Set up

To test this answer, you can have a simple Maven project with the following POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>

with a single class to compile sitting under src/main/java/test, being:

package test;

interface I {
void foo();
}
public class Main implements I {
public static void main(String[] args) {
new Main().foo();
}

@Override
public void foo() {
System.out.println("foo");
}
}

This looks like a standard Maven project configured to use JDK 5. Notice that the class uses @Override on a method implementing an interface. This was not allowed before Java 6.

If you try to build this project with Maven running under JDK 8, it will compile, despite setting <source>1.5</source>.

Why does it compile?

The Maven Compiler Plugin is not at fault. javac is to blame. Setting the -source flag does not tell javac to compile your project with this specific JDK version. It instructs javac to accept only a specific version of source code. From javac documentation:

-source release: Specifies the version of source code accepted.

For example, if you specified -source 1.4, then the source code you're trying to compile cannot contain generics, since those were introduced to the language later. The option enforces the source compatibility of your application. A Java application that uses Java 5 generics is not source compatible with a Java 4 program using a JDK 4 compiler. In the same way, an application using Java 8 lambda expressions is not source compatible to a JDK 6 compiler.

In this case, @Override is an annotation that was already present in Java 5. However, its semantics changed in Java 6. Therefore, code using @Override, whether it is on a method implementing an interface or not, is source compatible with a Java 5 program. As such, running a JDK 8 with -source 1.5 on such a class will not fail.

Why does it run?

Onto the second parameter: target. Again, this isn't a Maven Compiler concern, but a javac one. While the -source flag enforces source compatibility with an older version, -target enforces binary compatibility with an older version. This flag tells javac to generate byte code that is compatible with an older JVM version. It does not tell javac to check that the compiled code can actually run with the older JVM version. For that, you need to set a bootclasspath, which will cross-compile your code with a specified JDK.

Clearly, @Override on a method implementing an interface cannot run on a Java 5 VM, so javac should bark here. But nope: Override has source retention, meaning that the annotation is completely discarded after compilation has happened. Which also means that when cross-compilation is happening, the annotation isn't there anymore; it was discarded when compiling with JDK 8. As you found out, this is also why tools like the Animal Sniffer Plugin (which enables an automatic bootclasspath with pre-defined JDK versions) won't detect this: the annotation is missing.

In summary, you can package the sample application above with mvn clean package running on JDK 8, and run it without hitting any issues on a Java 5 JVM. It will print "foo".

How can I make it not compile?

There are two possible solutions.

The first, direct one, is to specify the path to javac through the executable property of the Compiler Plugin:

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
<compilerVersion>1.5</compilerVersion>
<fork>true</fork>
<!-- better to have that in a property in the settings, or an environment variable -->
<executable>/usr/lib/jvm/jdk1.5.0_22/bin/javac</executable>
</configuration>
</plugin>

This sets the actual version of the JDK the compiler should use with the compilerVersion parameter. This is a simple approach, but note that it only changes the JDK version used for compiling. Maven will still use the JDK 8 installation with which it is launched to generate the Javadoc or run the unit tests, or any step that would require a tool for the JDK installation.

The second, global, approach, is to use a toolchains. These will instruct Maven to use a JDK different than the one used to launch mvn, and every Maven plugins (or any plugin that is toolchains aware) will then use this JDK to perform their operation. Edit your POM file to add the following plugin configuration of the maven-toolchains-plugin:

<plugin>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>1.5</version>
</jdk>
</toolchains>
</configuration>
</plugin>

The missing ingredient is telling those plugins where the configuration for that toolchain is. This is done inside a toolchains.xml file, that is generally inside ~/.m2/toolchains.xml. Starting with Maven 3.3.1, you can define the location to this file using the --global-toolchains parameter, but best to keep it inside the user home. The content would be:

<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.5</version>
</provides>
<configuration>
<jdkHome>/usr/lib/jvm/jdk1.5.0_22</jdkHome>
</configuration>
</toolchain>
</toolchains>

This declares a toolchain of type jdk providing a JDK 5 with the path to the JDK home. The Maven plugins will now use this JDK. In effect, it will also be the JDK used when compiling the source code.

And if you try to compile again the sample project above with this added configuration... you'll finally have the error:

method does not override a method from its superclass

javac -Xlint:overrides not working

I believe you are misunderstanding the Xlint:overrides behaviour.

To my knowledge, enabling this check will cause the compiler to emit warnings (or maybe errors) when it encounters a method annotated with @Override that does not actually override a superclass method. It does not, however, check that all overridden methods are annotated correctly.

EDIT: Just tested it. The compiler will emit an error when you specify @Override on a method that does not override a superclass method, with or without the Xlint option.

The documentation on Oracle's website doesn't even mention the Xlint:overrides option so I'm guessing it's not implemented.

Eclipse ignoring incorrect @Override annotations

The definition of @Override changed slightly in Java 6 (and unfortunatly without proper documentation) wherein an @Override annotation on a method that implements an interface method is valid. In Java 5 that was considered an error.

Can I get Java 5 to ignore @Override errors?

Apparently you can upgrade to Java 1.5 u21 and this will fix the problem:

Why is javac failing on @Override annotation



Related Topics



Leave a reply



Submit