Consider the following code:
A.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
#interface A{}
C.java:
import java.util.*;
#A public class C {
public static void main(String[] args){
System.out.println(Arrays.toString(C.class.getAnnotations()));
}
}
Compiling and running works as expected:
$ javac *.java
$ java -cp . C
[#A()]
But then consider this:
$ rm A.class
$ java -cp . C
[]
I would've expected it to throw a ClassNotFoundException, since #A is missing. But instead, it silently drops the annotation.
Is this behaviour documented in the JLS somewhere, or is it a quirk of Sun's JVM? What's the rationale for it?
It seems convenient for things like javax.annotation.Nonnull (which seems like it should've been #Retention(CLASS) anyway), but for many other annotations it seems like it could cause various bad things to happen at runtime.
In the earlier public drafts for JSR-175 (annotations), it was discussed if the compiler and runtime should ignore unknown annotations, to provide a looser coupling between the usage and declaration of annotations. A specific example was the use of applications server specific annotations on an EJB to control the deployment configuration. If the same bean should be deployed on a different application server, it would have been convenient if the runtime simply ignored the unknown annotations instead of raising a NoClassDefFoundError.
Even if the wording is a little bit vague, I assume that the behaviour you are seeing is specified in JLS 13.5.7: "... removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language." I interpret this as if annotations are removed (not available at runtime), the program should still link and run and that this implies that the unknown annotations are simply ignored when accessed through reflection.
The first release of Sun's JDK 5 did not implement this correctly, but it was fixed in 1.5.0_06. You can find the relevant bug 6322301 in the bug database, but it does not point to any specifications except claiming that "according to the JSR-175 spec lead, unknown annotations must be ignored by getAnnotations".
Quoting the JLS:
9.6.1.2 Retention Annotations may be present only in the source code, or
they may be present in the binary form
of a class or interface. An annotation
that is present in the binary may or
may not be available at run-time via
the reflective libraries of the Java
platform.
The annotation type
annotation.Retention is used to choose
among the above possibilities. If an
annotation a corresponds to a type T,
and T has a (meta-)annotation m that
corresponds to annotation.Retention,
then:
If m has an element whose value is annotation.RetentionPolicy.SOURCE,
then a Java compiler must ensure that
a is not present in the binary
representation of the class or
interface in which a appears.
If m has an element whose value is annotation.RetentionPolicy.CLASS, or
annotation.RetentionPolicy.RUNTIME a
Java compiler must ensure that a is
represented in the binary
representation of the class or
interface in which a appears, unless m
annotates a local variable
declaration. An annotation on a local
variable declaration is never retained
in the binary representation.
If T does not have a (meta-)annotation
m that corresponds to
annotation.Retention, then a Java
compiler must treat T as if it does
have such a meta-annotation m with an
element whose value is
annotation.RetentionPolicy.CLASS.
So RetentionPolicy.RUNTIME ensures that the annotation is compiled into the binary but an annotation present in the binary doesn't have to be available at runtime
if you actually have code that reads #A and does something with it, the code has a dependency on class A, and it will throw ClassNotFoundException.
if not, i.e. no code cares specificly about #A, then it's arguable that #A doesn't really matter.
Related
While reading about the multiple inheritance or diamond problem in Java I realized that it is not supported. But I wonder how does Java actually restricts multiple inheritance?
Is there any class to check if the programmer is passing more than one class name after extends keyword or some other way to detect this functionality?
I read few articles but nothing suggest about how exactly Java prevents Multiple inheritance except the one common answer that it throws an error: classname is inheriting multiple classes.
But I wonder how does Java actually restricts multiple inheritance?
It is disallowed at the syntax level. The syntax for a class declaration allows one class name after the extends keyword. And the names in the implements list must be interface names not class names. See Section 8.1 Class Declarations of the JLS. The compiler checks both of these things. Java source code that attempts to declare multiple super-classes will not compile.
At the implementation level, the format for a ".class" file only allows one class to be listed as the super_class; see the ClassFile structure in Section 4.1 of the JVM spec. The identifiers in the interfaces must all refer to interfaces. The various classfile constraints specified in the JVM spec are enforced by the JVM's native classloader.
If you want to see how these restrictions are enforced, you can download an OpenJDK source tree and read the code for yourself. (I don't see the point though. All you really need to know is that the restrictions are strictly enforced and there is no practical way to get around that enforcement.)
If you try to extend more than one class, the compiler will actually complain, and state error: '{' expected. If you are interested in what part of the JDK actually does this, I suggest taking a look at the OpenJDK sources. Source code for the javac parser can be found here.
As a side note, Java disallows multiple inheritance of state, which is what you are referring to. You can still achieve multiple inheritance of behavior through implementing multiple interfaces, though.
I noticed a discrepancy between Eclipse's compiler and javac while using repeating annotations. The repeating annotation and its container were in the same package, but the former was declared public, while the latter remained package-private. Eclipse had no problem with the arrangement, even though the repeating annotation was referenced in another package. javac, on the other hand, refused to compile, saying
value() in [container] is defined in an inaccessible class or interface
My question is, which one is correct? I couldn't find any rule about this in the JLS. Does that mean it's open to interpretation? Or is there a bug in one of the compilers?
There is no statement regarding accessibility but the specification makes it clear that it is possible that a repeatable annotation may be restricted to be repeatable at certain locations only, due to the way how the containing annotation has been declared.
JLS §9.6.3
…
T is applicable to at least the same kinds of program element as TC (§9.6.4.1). Specifically, if the kinds of program element where T is applicable are denoted by the set m1, and the kinds of program element where TC is applicable are denoted by the set m2, then each kind in m2 must occur in m1, …
This clause implements the policy that an annotation type may be repeatable on only some of the kinds of program element where it is applicable
This is also backed with an example:
Example 9.6.3-2. Restricting Where Annotations May Repeat
An annotation whose type declaration indicates a target of java.lang.annotation.ElementType.TYPE can appear in at least as many locations as an annotation whose type declaration indicates a target of java.lang.annotation.ElementType.ANNOTATION_TYPE. For example, given the following declarations of repeatable and containing annotation types:
#Target(ElementType.TYPE)
#Repeatable(FooContainer.class)
#interface Foo {}
#Target(ElementType.ANNOTATION_TYPE)
#Interface FooContainer {
Foo[] value();
}
#Foo can appear on any type declaration while #FooContainer can appear on only annotation type declarations. Therefore, the following annotation type declaration is legal:
#Foo #Foo
#interface X {}
while the following interface declaration is illegal:
#Foo #Foo
interface X {}
While this is discussing restrictions imposed by #Target rather than accessibility modifiers, it describes a “spirit” that can be applied to the latter as well. The intention clearly seems to be that a repeatable annotation can only be repeated if the properties of the specified container annotation allow it, otherwise the annotation still may be used, but only with a single occurrence.
It’s not like the properties of a repeatable annotation were capable of overriding the properties of the container annotation type. This is in line with the behavior at all other places; even if there is no reference to the inaccessible annotation type in the source code, compiling the repeated annotation would create a reference in the class file and a class file may not contain symbolic references to inaccessible classes and there shouldn’t be an exception only for container annotations.
Section 4.7.16 of the JVM specification includes a description of "RuntimeVisibleAnnotations". I am wondering what can cause an attribute to be included in this attributes table, is this only by applying #Retention(RetentionPolicy.RUNTIME) on an attribute? Conversely, for "RuntimeInvisibleAnnotations" (see further in 4.7.17) is this #Retention(RetentionPolicy.CLASS) only or is it also #Retention(RetentionPolicy.SOURCE) ?
Compiling information from the JVM and JLS specifications gives us the following picture:
Annotations meta-annotated with the #Retention whose value is RetentionPolicy.SOURCE must not be present in the binary representation of the class or interface in which they appear, i.e. they are not to be recorded in the class file at all.
Annotations with the RetentionPolicy.CLASS must be represented in the binary representation of the class or interface in which they appear, unless they annotate a local variable declaration. An annotation on a local variable declaration is never retained in the binary representation.
So this is what the RuntimeInvisibleAnnotations attribute is designed for.
They need not be retained by the VM at run time, unless the Java Virtual Machine has been instructed to retain these annotations via some implementation-specific mechanism such as a command line flag.
Annotations with the RetentionPolicy.RUNTIME are to be recorded in the class file by the compiler and must be available at run time via reflection libraries. This is for the RuntimeVisibleAnnotations attribute.
For this source code ...
... Eclipse reports the following error:
Only a type can be imported. pkg.a resolves to a package
... while Suns javac compiles it fine.
Similar situation if I try to fully qualify the class pkg.a like this:
Eclipse reports...
pkg.a cannot be resolved to a type
... while Suns javac compiles it fine.
It seems like Eclipse favors interpreting an identifier as a package over a class name, while javac does the exact opposite. So, is it a bug in Eclipse or in javac?
(A reference to the language specification is obviously a plus.)
It is definitely an Eclipse bug:
6.4.2. Obscuring
A simple name may occur in contexts where it may potentially be interpreted as the name of a variable, a type, or a package. In these situations, the rules of §6.5 specify that a variable will be chosen in preference to a type, and that a type will be chosen in preference to a package. Thus, it is may sometimes be impossible to refer to a visible type or package declaration via its simple name. We say that such a declaration is obscured.
The clear implication is that it is legal for a class and a package to have the same name. Otherwise, the JLS would say that a class obscuring a package was illegal ... not that the class is used in preference.
Having said that, the only reason you've gotten into this situation is that you've chosen to ignore Java's conventions on naming. Don't expect much sympathy ...
It's a styling convention to give classes a name starting with a capital letter and packages with a lower case letter; that would solve your problem.
I want to know how Java linker works. Specifically, in which order it combines classes, interfaces, packages, methods and etc into jvm-executable format. I have found some information here, but there is not so much information about linking order.
There is no such thing as a Java "linker". There is, however, the concept of a classloader which - given an array of java byte codes from "somewhere" - can create an internal representation of a Class which can then be used with new etc.
In this scenario interfaces are just special classes. Methods and fields are available when the class has been loaded.
First of all: methods are always part of a class. Interfaces are basically just special classes, and packages are just a part of the fully qualified name of a class with some impact on visibility and the physical organization of class files.
So the question comes down to: how does a JVM link class files? The JVM spec you linked to says:
The Java programming language allows
an implementation flexibility as to
when linking activities (and, because
of recursion, loading) take place,
provided that the semantics of the
language are respected, that a class
or interface is completely verified
and prepared before it is initialized,
and that errors detected during
linkage are thrown at a point in the
program where some action is taken by
the program that might require linkage
to the class or interface involved in
the error.
For example, an implementation may
choose to resolve each symbolic
reference in a class or interface
individually, only when it is used
(lazy or late resolution), or to
resolve them all at once, for example,
while the class is being verified
(static resolution). This means that
the resolution process may continue,
in some implementations, after a class
or interface has been initialized.
Thus, the question can only be answered for a specific JVM implementation.
Furthermore, it should never make a difference in the behaviour of Java programs, except possibly for the exact point where linking errors result in runtime Error instances being thrown.
Java doesn't do linking the way C does. The principle unit is the class definition. A lot of the matching of a class reference to its definition happens at runtime. So you could compile a class against one version of a library, but provide another version at runtime. If the relevant signatures match, everything will be ok. There's some in-lining of constants at compile time, but that's about it.
As noted previously Java compiler doesn't have a linker. However, JVM has a linking phase, which performed after class loading. JVM spec defines it at best:
Linking a class or interface involves verifying and preparing that
class or interface, its direct superclass, its direct superinterfaces,
and its element type (if it is an array type), if necessary.
Resolution of symbolic references in the class or interface is an
optional part of linking.
This specification allows an implementation flexibility as to when
linking activities (and, because of recursion, loading) take place,
provided that all of the following properties are maintained:
A class or interface is completely loaded before it is linked.
A class or interface is completely verified and prepared before it is
initialized.
Errors detected during linkage are thrown at a point in the program
where some action is taken by the program that might, directly or
indirectly, require linkage to the class or interface involved in the
error.
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4
Linking is one of the three activities performed by ClassLoaders. It includes verification, preparation, and (optionally) resolution.
Verification : It ensures the correctness of .class file i.e. it check whether this file is properly formatted and generated by valid compiler or not. If verification fails, we get run-time exception java.lang.VerifyError.
Preparation : JVM allocates memory for class variables and initializing the memory to default values.
Resolution : It is the process of replacing symbolic references from the type with direct references. It is done by searching into method area to locate the referenced entity.