Java Annotations - looking for an example of RetentionPolicy.CLASS - java

according to Java Annotation API:
RetentionPolicy.CLASS
Annotations are to be recorded in the class file by the
compiler but need not be retained by
the VM at run time.
RetentionPolicy.RUNTIME
Annotations are to be recorded in the class file by the
compiler and retained by the VM at run
time, so they may be read
reflectively.
I am looking for a sample of "CLASS" retention policy. when we need to use this policy instead of RUNTIME policy.

CLASS Annotations are used in obfuscator tools like http://proguard.sourceforge.net .
For example annotation #KeepName disables name mangling when you need to have your class name unchanged to be able to call methods like Class.forName().

Of all of the large number of libraries I have in my current project. the only examples I can find are in the Google Guava library, for example com.google.common.annotations.GwtCompatible.
I'm not really sure why they chose this retention policy, though - perhaps for tools support, where the tools read the class files themselves, rather than going through the reflection API. I'm not sure that I really see the point of this distinction, though.

RetentionPolicy.CLASS are Useful when doing byte code-level post-processing.
Example :
https://github.com/thesmythgroup/DroidCook/blob/master/src/org/tsg/android/api/Annotations.java
http://retroweaver.sourceforge.net/

Minimal example
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.CLASS)
#interface RetentionClass {}
#Retention(RetentionPolicy.RUNTIME)
#interface RetentionRuntime {}
public static void main(String[] args) {
#RetentionClass
class C {}
assert C.class.getAnnotations().length == 0;
#RetentionRuntime
class D {}
assert D.class.getAnnotations().length == 1;
}
If we use javap on the annotated classes, we see that the Retention.CLASS annotated class gets a RuntimeInvisible class attribute:
#14 = Utf8 LRetentionClass;
[...]
RuntimeInvisibleAnnotations:
0: #14()
while Retention.RUNTIME annotation gets a RuntimeVisible class attribute:
#14 = Utf8 LRetentionRuntime;
[...]
RuntimeVisibleAnnotations:
0: #14()
So the information is present on both cases in the bytecode.
Therefore, Runtime.CLASS can be used to associate arbitrary metadata to a class which bytecode manipulation tools can use, without interfering with runtime-visible behavior.
Examples on GitHub for you to play with.

Related

Create a custom annotation for springboot beans [duplicate]

Can anyone explain in a clear way the practical differences between the java.lang.annotation.RetentionPolicy constants SOURCE, CLASS, and RUNTIME?
I'm also not exactly sure what the phrase "retaining annotation" means.
RetentionPolicy.SOURCE: Discard during
the compile. These annotations don't
make any sense after the compile has
completed, so they aren't written to
the bytecode.
Example: #Override, #SuppressWarnings
RetentionPolicy.CLASS: Discard during
class load. Useful when doing
bytecode-level post-processing.
Somewhat surprisingly, this is the
default.
RetentionPolicy.RUNTIME: Do not
discard. The annotation should be
available for reflection at runtime.
Example: #Deprecated
Source:
The old URL is dead now
hunter_meta and replaced with hunter-meta-2-098036. In case even this goes down, I am uploading the image of the page.
Image (Right Click and Select 'Open Image in New Tab/Window')
According to your comments about class decompilation, here is how I think it should work:
RetentionPolicy.SOURCE: Won't appear in the decompiled class
RetentionPolicy.CLASS: Appear in the decompiled class, but can't be inspected at run-time with reflection with getAnnotations()
RetentionPolicy.RUNTIME: Appear in the decompiled class, and can be inspected at run-time with reflection with getAnnotations()
Minimal runnable example
Language level:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.SOURCE)
#interface RetentionSource {}
#Retention(RetentionPolicy.CLASS)
#interface RetentionClass {}
#Retention(RetentionPolicy.RUNTIME)
#interface RetentionRuntime {}
public static void main(String[] args) {
#RetentionSource
class B {}
assert B.class.getAnnotations().length == 0;
#RetentionClass
class C {}
assert C.class.getAnnotations().length == 0;
#RetentionRuntime
class D {}
assert D.class.getAnnotations().length == 1;
}
Bytecode level: using javap we observe that the Retention.CLASS annotated class gets a RuntimeInvisible class attribute:
#14 = Utf8 LRetentionClass;
[...]
RuntimeInvisibleAnnotations:
0: #14()
while Retention.RUNTIME annotation gets a RuntimeVisible class attribute:
#14 = Utf8 LRetentionRuntime;
[...]
RuntimeVisibleAnnotations:
0: #14()
and the Runtime.SOURCE annotated .class does not get any annotation.
Examples on GitHub for you to play with.
Retention Policy: A retention policy determines at what point an annotation is discarded. It is s specified using Java's built-in annotations: #Retention[About]
1.SOURCE: annotation retained only in the source file and is discarded
during compilation.
2.CLASS: annotation stored in the .class file during compilation,
not available in the run time.
3.RUNTIME: annotation stored in the .class file and available in the run time.
CLASS
:Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
RUNTIME
:Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
SOURCE
:Annotations are to be discarded by the compiler.
Oracle Doc

How will I know the annotations of a Java class?

I need to know the annotations of a Java class. I am using Lombok.
Sample is:
#Data
#Builder
public class JavaBean {}
I tried java.lang.annotation.Annotation[] annotation = JavaBean.class.getAnnotations but it doesn't show Data and Builder.
I think you cannot see the annotations in JavaBean.class.getAnnotations because the #Retention is equals to SOURCE.
This kind of annotation is not needed at runtime.
For more details : Annotation SOURCE Retention Policy
Have a good day.
The answer is in source of these annotations:
#Target({TYPE, METHOD, CONSTRUCTOR})
#Retention(SOURCE)
public #interface Builder {
.....
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface Data {
.....
Your code to get annotation is correct but it's #Retention(RetentionPolicy.SOURCE) which is playing role here.
Java defined 3 types of retention policies through java.lang.annotation.RetentionPolicy enumeration. It has SOURCE, CLASS and RUNTIME.
1) Annotation with retention policy SOURCE will be retained only with source code, and discarded during compile time.
2) Annotation with retention policy CLASS will be retained till compiling the code, and discarded during runtime.
3) Annotation with retention policy RUNTIME will be available to the JVM through runtime.
#Data and #Builder are marked with #Retention(SOURCE) which means these annotations are not present at runtime with your class hence you are not able to get these annotations..
Lombok annotations are pre-processed before the actual compilation, thus the compiled classes do not contain them as annotation, but rather as the already generated code.

How do different retention policies affect my annotations?

Can anyone explain in a clear way the practical differences between the java.lang.annotation.RetentionPolicy constants SOURCE, CLASS, and RUNTIME?
I'm also not exactly sure what the phrase "retaining annotation" means.
RetentionPolicy.SOURCE: Discard during
the compile. These annotations don't
make any sense after the compile has
completed, so they aren't written to
the bytecode.
Example: #Override, #SuppressWarnings
RetentionPolicy.CLASS: Discard during
class load. Useful when doing
bytecode-level post-processing.
Somewhat surprisingly, this is the
default.
RetentionPolicy.RUNTIME: Do not
discard. The annotation should be
available for reflection at runtime.
Example: #Deprecated
Source:
The old URL is dead now
hunter_meta and replaced with hunter-meta-2-098036. In case even this goes down, I am uploading the image of the page.
Image (Right Click and Select 'Open Image in New Tab/Window')
According to your comments about class decompilation, here is how I think it should work:
RetentionPolicy.SOURCE: Won't appear in the decompiled class
RetentionPolicy.CLASS: Appear in the decompiled class, but can't be inspected at run-time with reflection with getAnnotations()
RetentionPolicy.RUNTIME: Appear in the decompiled class, and can be inspected at run-time with reflection with getAnnotations()
Minimal runnable example
Language level:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.SOURCE)
#interface RetentionSource {}
#Retention(RetentionPolicy.CLASS)
#interface RetentionClass {}
#Retention(RetentionPolicy.RUNTIME)
#interface RetentionRuntime {}
public static void main(String[] args) {
#RetentionSource
class B {}
assert B.class.getAnnotations().length == 0;
#RetentionClass
class C {}
assert C.class.getAnnotations().length == 0;
#RetentionRuntime
class D {}
assert D.class.getAnnotations().length == 1;
}
Bytecode level: using javap we observe that the Retention.CLASS annotated class gets a RuntimeInvisible class attribute:
#14 = Utf8 LRetentionClass;
[...]
RuntimeInvisibleAnnotations:
0: #14()
while Retention.RUNTIME annotation gets a RuntimeVisible class attribute:
#14 = Utf8 LRetentionRuntime;
[...]
RuntimeVisibleAnnotations:
0: #14()
and the Runtime.SOURCE annotated .class does not get any annotation.
Examples on GitHub for you to play with.
Retention Policy: A retention policy determines at what point an annotation is discarded. It is s specified using Java's built-in annotations: #Retention[About]
1.SOURCE: annotation retained only in the source file and is discarded
during compilation.
2.CLASS: annotation stored in the .class file during compilation,
not available in the run time.
3.RUNTIME: annotation stored in the .class file and available in the run time.
CLASS
:Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
RUNTIME
:Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
SOURCE
:Annotations are to be discarded by the compiler.
Oracle Doc

Overloaded package-private method causes compilation failure - Is this a JLS oddity or javac bug?

I've come across an oddity of the JLS, or a JavaC bug (not sure which). Please read the following and provide an explanation, citing JLS passage or Sun Bug ID, as appropriate.
Suppose I have a contrived project with code in three "modules" -
API - defines the framework API - think Servlet API
Impl - defines the API implementation - think Tomcat Servlet container
App - the application I wrote
Here are the classes in each module:
API - MessagePrinter.java
package api;
public class MessagePrinter {
public void print(String message) {
System.out.println("MESSAGE: " + message);
}
}
API - MessageHolder.java (yes, it references an "impl" class - more on this later)
package api;
import impl.MessagePrinterInternal;
public class MessageHolder {
private final String message;
public MessageHolder(String message) {
this.message = message;
}
public void print(MessagePrinter printer) {
printer.print(message);
}
/**
* NOTE: Package-Private visibility.
*/
void print(MessagePrinterInternal printer) {
printer.print(message);
}
}
Impl - MessagePrinterInternal.java - This class depends on an API class. As the name suggests, it is intended for "internal" use elsewhere in my little framework.
package impl;
import api.MessagePrinter;
/**
* An "internal" class, not meant to be added to your
* application classpath. Think the Tomcat Servlet API implementation classes.
*/
public class MessagePrinterInternal extends MessagePrinter {
public void print(String message) {
System.out.println("INTERNAL: " + message);
}
}
Finally, the sole class in the App module...MyApp.java
import api.MessageHolder;
import api.MessagePrinter;
public class MyApp {
public static void main(String[] args) {
MessageHolder holder = new MessageHolder("Hope this compiles");
holder.print(new MessagePrinter());
}
}
So, now I attempt to compile my little application, MyApp.java. Suppose my API jars are exported via a jar, say api.jar, and being a good citizen I only referencd that jar in my classpath - not the Impl class shiped in impl.jar.
Now, obviously there is a flaw in my framework design in that the API classes shouldn't have any dependency on "internal" implementation classes. However, what came as a surprise is that MyApp.java didn't compile at all.
javac -cp api.jar src\MyApp.java
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found
holder.print(new MessagePrinter());
^
1 error
The problem is that the compiler is trying to resolve the version print() to use, due to method overloading. However, the compilation error is somewhat unexpected, as one of the methods is package-private, and therefore not visible to MyApp.
So, is this a javac bug, or some oddity of the JLS?
Compiler: Sun javac 1.6.0_14
There is is nothing wrong with JLS or javac. Of course this doesn't compile, because your class MessageHolder references MessagePrinterInternal which is not on the compile classpath if I understand your explanation right. You have to break this reference into the implementation, for example with an interface in your API.
EDIT 1: For clarification: This has nothing to do with the package-visible method as you seem to think. The problem is that the type MessagePrinterInternal is needed for compilation, but you don't have it on the classpath. You cannot expect javac to compile source code when it doesn't have access to referenced classes.
EDIT 2: I reread the code again and this is what seems to be happening: When MyApp is compiled, it tries to load class MessageHolder. Class MessageHolder references MessagePrinterInternal, so it tries to load that also and fails. I am not sure that is specified in the JLS, it might also depend on the JVM. In my experience with the Sun JVM, you need to have at least all statically referenced classes available when a class is loaded; that includes the types of fields, anything in the method signatures, extended classses and implemented interfaces. You could argue that this is counter-intuitive, but I would respond that in general there is very little you do with a class where such information is missing: you cannot instantiate objects, you cannot use the metadata (the Class object) etc. With that background knowledge, I would say the behavior you see is expected.
First off I would expect the things in the api package to be interfaces rather than classes (based on the name). Once you do this the problem will go away since you cannot have package access in interfaces.
The next thing is that, AFAIK, this is a Java oddity (in that it doesn't do what you would want). If you get rid of the public method and make the package on private you will get the same thing.
Changing everything in the api package to be interfaces will fix your problem and give you a cleaner separation in your code.
I guess you can always argue that javac can be a little bit smarter, but it has to stop somewhere. it's not human, human can always be smarter than a compiler, you can always find examples that make perfect sense for a human but dumbfound a compiler.
I don't know the exact spec on this matter, and I doubt javac authors made any mistake here. but who cares? why not put all dependencies in the classpath, even if some of them are superficial? doing that consistently makes our lives a lot easier.

In java how do you refer to a class that is in the default package of a third party library?

I have downloaded a third party library and they have classes I need to refer to in the default package? How do I import these classes?
It's not possible directly with the compiler. Sun removed this capability. If something is in the default namespace, everything must be in the default namespace.
However, you can do it using the ClassLoader. Assuming the class is called Thirdparty, and it has a static method call doSomething(), you can execute it like this:
Class clazz = ClassLoader.getSystemClassLoader().loadClass("Thirdparty");
java.lang.reflect.Method method = clazz.getMethod("doSomething");
method.invoke(null);
This is tedious to say the least...
Long ago, sometime before Java 1.5, you used to be able to import Thirdparty; (a class from the unnamed/default namespace), but no longer. See this Java bug report. A bug report asking for a workaround to not being able to use classes from the default namespace suggests to use the JDK 1.3.1 compiler.
To avoid the tedious method.invoke() calls, I adapted the above solution:
Write an interface for the desired functionality in your desired my.package
package my.package;
public interface MyAdaptorInterface{
public void function1();
...
}
Write an adaptor in the default package:
public class MyAdaptor implements my.package.MyAdaptorInterface{
public void function1(){thirdparty.function1();}
...
}
Use ClassLoader/Typecast to access object from my.package
package my.package;
Class clazz = ClassLoader.getSystemClassLoader().loadClass("MyAdaptor");
MyAdaptorInterface myObj = (MyAdaptorInterface)clazz.newInstance();
myObj.function1();

Categories

Resources