I`d like to create my own annotations to annotate some local variable. To write the annotation is not the problem, the problem is to get the information of them at the Runtime. I could only get some information from annotated methods or method parameters, but not from local variables. Is there any way to get it?
My own annotation is something like that:
public void m(int a)
#MyOwnAnnotation(some information)
int b = 5;
}
Or, as an alternative, is there any way to get the code of the method, to parse it further and finally get annotation value?
Thanks in advance.
With reflection you can't retrieve a local variable. So you can't retrieve an annotation on a local variable via reflection. I think that this kind of annotation is only used for compiler warnings.
You can look http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html
Local variable annotations are not retained in class files (or at runtime) regardless of the retention policy set on the annotation type. See JLS 9.6.1.2.
If you wan't to retrieve method code, you can use JavaParser (http://javaparser.org/).
As of Java 8, local variable annotations are retained in class files. As noted by Erick Hagstrom, this long-standing bug was fixed by JSR 308, which also added type annotations to the Java language.
However, Java's reflection API has not been updated to give access within method bodies. You will need to parse the classfile yourself. You can use a tool such as ASM. EDIT: I don't recommend JavaParser, because it has not been updated beyond Java 1.5. JavaParser has been updated.
JLS 9.6.1.2 does indeed state that local variable annotations are not retained. However, JSR 308 is working its way through the community process. It should give you the capability you need.
If you want an interim solution, here is an implementation of JSR 308.
At the moment, as mentioned in some other posts, you cannot retrieve a local variable value simply from the annotation alone.
However, I did have a simimlar issue and managed to come up with a solution using fields. Sample code is below:
Interface Class
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface test{
}
PoJo
public class testObject{
#test
private String one;
private String two;
//Getters and Setters
}
Get Object values
public void getFields (Object obj){
Field fields = obj.getClass().getDeclaredFields();
for (Field f : fields){
test fieldAnnotation = f.getAnnotation(test.Class);
if (fieldAnnotation != null){
f.get(obj);
// Do things here based on the values
}
}
}
Main Class
public static void main(String[] args){
//Create object
testObject test = new testObject();
test.setOne("testOne");
test.setTwo("testTwo");
getFields(test);
}
Hopefully this helps in explaining how you can get the fields for an object based on the annotation. You are simply using the annotation to 'mark' the fields you want to retrieve and then reading the value from the object.
Related
Suppose you have some annotation Annot:
#Retention(/*your retention policy*/)
#Target(/*targeted element type*/)
public #interface Annot {
String value() default "Hello World!";
}
And in some related code, say, an annotation processor, you need the default value of the value() Annotation field without having access to a class that is annotated with #Annot. Of course you could simply do
public static final String ANNOT_VALUE_DEFAULT = "Hello World!";
in your processor class, then change the following in #Annot:
String value() default Processor.ANNOT_VALUE_DEFAULT;
(Processor being the class name of your annotation processor). While this works fine with Strings, the change in #Annot fails when your value() type is an enum. It might fail for other values, too, but enum is part of my use case, therefore if this doesn't work, it doesn't matter if other types will work.
Now, of course, the simplest way to resolve this is to simply have the default value specified in #Annot and Processor, separately. But every programmer knows that duplicated constants are not a good idea in general. You might want to automatically reflect changes in one part (e.g. #Annot) in the other parts (e.g. Processor). For this to work, you'd have to be able to do this:
var defaultVal = Annot.value(); // statically (without an instance annotated with #Annot) access default value
So, is this static access in any way possible?
Partial solution
It is not urgent for me to find a solution right now as I already found a semi-convenient workaround (see my answer). Still, because the workaround is a bit "hacky", I want to know if there is a more elegant way to do this.
Workaround
If it turns out that there is no satisfying solution to this, but you, the reader, really need a solution, take a look at this workaround:
So, the problem is that you want to access the default value without being supplied with a class that is annotated with #Annot. Well, who says that you don't have access to such a class? Just add this (preferably package-private) class to your source code:
#Annot
class DefaultAnnotValues {
private static final Annot ANNOT = DefaultAnnotValue.class.getAnnotation(Annot.class);
static SomeEnum value = ANNOT.value();
// add all other enum fields with default values here
private DefaultAnnotValues() {
}
}
Now you can access all default values of your annotation, and when you change a default value in the annotation definition, it will be automatically reflected to wherever you use those defaults.
In https://dagger.dev/multibindings.html, I read about #AutoAnnotation. It has a reference to https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/AutoAnnotation.java.
It is also mentioned in
https://github.com/google/auto/blob/57dfad360306619a820e6aae4a14a1aa67c29299/value/userguide/howto.md#annotation
I read about it, can't get to understand it.
I manage to access it from my Android code using
implementation 'com.google.auto.value:auto-value:1.5.2'
kapt 'com.google.auto.value:auto-value:1.5.2'
And also
android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
But I don't understand how it could be used. Is there any good tutorial of using it?
AutoAnnotation automatically generates a class that implements an annotation interface in the same way the JDK does.
Dagger map keys
When using a Multibindings map through Dagger that uses a custom annotation as its key, Dagger will install the instance T or Provider Provider<T> into the returned map, using the annotation instance itself as the key. To make this clearer:
#MapKey
#interface YourAnnotation {
String foo();
}
#Provides #YourAnnotation(foo="bar") YourClass getYourClassForBar() { /* ... */ }
// Dagger will create a multibinding that would allow you to inject this:
#Inject Map<YourAnnotation, YourClass> map;
If the only thing that matters here is foo, you could also use unwrapKeys to make the map keyed by String instead of YourAnnotation, but let's assume you want this because you want YourAnnotation to have multiple values in the future. But where do the implementations of YourAnnotation come from, and how are you supposed to call get on the map?
Annotations at runtime
When you annotate a Java element (frequently a class, method, or field) Java will return a particular implementation of that class's annotation. From the Java tutorial:
#interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
// [...]
#ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
// Note array notation
reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {/* ... */}
In this usage, Generation3List has one Annotation of type ClassPreamble. If the annotation is retained at runtime (i.e. ClassPreamble itself is annotated with #Retention(RUNTIME)), you can get to it via Generation3List.class.getAnnotations() or Generation3List.class.getAnnotation(ClassPreamble.class). (There are also declared counterparts that handle superclass annotations differently.)
Once you get to an instance of ClassPreamble, you can use the methods like author() and date() to retrieve the data out of the class. However, ClassPreamble behaves as an interface, and the implementation of that annotation is internal to the VM. That makes it more difficult to create your own arbitrary instance of ClassPreamble at runtime.
Conforming annotation implementations
Because YourAnnotation and ClassPreamble are interfaces, you could just create an implementation. However, that implementation is unlikely to have matching implementations of equals and hashCode compared to the VM's implementation, because the implementation may vary between JREs and may also vary in Android. However, the implementation of equals and hashCode is actually very closely prescribed in the docs for Annotation:
The hash code of an annotation is the sum of the hash codes of its members (including those with default values), as defined below: The hash code of an annotation member is (127 times the hash code of the member-name as computed by String.hashCode()) XOR the hash code of the member-value, as defined below [...]
Returns true if the specified object represents an annotation that is logically equivalent to this one. In other words, returns true if the specified object is an instance of the same annotation type as this instance, all of whose members are equal to the corresponding member of this annotation, as defined below [...]
It is possible to manually implement these rules, but it would be difficult to do so, and it would also impose a burden if the structure of YourAnnotation or ClassPreamble were to change. Though there are reflective solutions to this problem, AutoAnnotation generates the code for a conforming implementation automatically:
public class YourAnnotations {
#AutoAnnotation public static YourAnnotation yourAnnotation(String foo) {
return new AutoAnnotation_YourAnnotations_yourAnnotation(foo);
}
}
public class ClassPreambles {
#AutoAnnotation public static ClassPreamble classPreamble(
String author,
String date,
int currentRevision,
String lastModified,
String lastModifiedBy,
String[] reviewers) {
return new AutoAnnotation_ClassPreambles_classPreamble(
author,
date,
currentRevision,
lastModified,
lastModifiedBy,
reviewers);
}
}
With AutoAnnotation's generated implementation, you can call get on the map that Dagger Multibindings generates (or provide test implementations you control) without having to deal with Annotation-specific hashCode XORs or equals rules. This is useful beyond Dagger and tests, but because Dagger uses annotation instances in its maps, it makes sense that you might need to use AutoAnnotation to create similar instances.
Lombok.val allows you to
use val as the type of a local variable declaration instead of
actually writing the type. When you do this, the type will be inferred from the initializer expression. The local variable will also be made final.
So instead of
final ArrayList<String> example = new ArrayList<String>();
You can write
val example = new ArrayList<String>();
I've tried to do some research into how this actually works but there doesn't seem to be a huge amount of information. Looking at the github page, I can see that val is an annotation type. The annotation type is then used, rather than an actual annotation.
I wasn't even aware you could even use annotation types in this manner but upon testing it the following code is indeed valid. However, I'm still not sure why you would ever want to use the type in this way.
public class Main
{
public #interface Foo { }
public static void main(String... args)
{
Foo bar;
System.out.println("End");
}
}
How does Lombok process these usages if they are not annotations, but annotation types? To my (obviously incorrect) understanding, the syntax should look more like:
#Val foo = new ArrayList<String>();
(I'm aware constraints of annotations mean the above is not valid syntax)
In order for Lombok to work, the source code needs to parse without errors. That's why, as you already mentioned, #val foo = new ArrayList<String>(); wouldn't work.
Although Lombok uses annotations, and an annotation processor, the annotation processor is only used as a means to get involved by the compiler.
Lombok does not have a registered processor for #val. Instead, it processes all java files, visits the whole AST and replaces val by the type of the initialization expression of the local variables.
For the actual replacement, for Eclipse/ecj see this class and this one. For javac, see this class.
Disclosure: I am a Lombok developer.
There is a constructor with three parameters of type enum:
public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3)
{...}
The three parameters of type enum are not allowd to be combined with all possible values:
Example:
EnumType1.VALUE_ONE, EnumType2.VALUE_SIX, EnumType3.VALUE_TWENTY is a valid combination.
But the following combination is not valid:
EnumType1.VALUE_TWO, EnumType2.VALUE_SIX, EnumType3.VALUE_FIFTEEN
Each of the EnumTypes knows with which values it is allowed to be combined:
EnumType1 and the two others implement a isAllowedWith() method to check that as follows:
public enum EnumType1 {
VALUE_ONE,VALUE_TWO,...;
public boolean isAllowedWith(final EnumType2 type) {
switch (this) {
case VALUE_ONE:
return type.equals(Type.VALUE_THREE);
case VALUE_TWO:
return true;
case VALUE_THREE:
return type.equals(Type.VALUE_EIGHT);
...
}
}
I need to run that check at compile time because it is of extreme importance in my project that the combinations are ALWAYS correct at runtime.
I wonder if there is a possibility to run that check with user defined annotations?
Every idea is appreciated :)
The posts above don't bring a solution for compile-time check, here's mine:
Why not use concept of nested Enum.
You would have EnumType1 containing its own values + a nested EnumType2 and this one a nested EnumType3.
You could organize the whole with your useful combination.
You could end up with 3 classes (EnumType1,2 and 3) and each one of each concerned value containing the others with the allowed associated values.
And your call would look like that (with assuming you want EnumType1.VALUE_ONE associated with EnumType2.VALUE_FIFTEEN) :
EnumType1.VALUE_ONE.VALUE_FIFTEEN //second value corresponding to EnumType2
Thus, you could have also: EnumType3.VALUE_SIX.VALUE_ONE (where SIX is known by type3 and ONE by type1).
Your call would be change to something like:
public SomeClass(EnumType1 enumType)
=> sample:
SomeClass(EnumType1.VALUE_ONE.VALUE_SIX.VALUE_TWENTY) //being a valid combination as said
To better clarify it, check at this post: Using nested enum types in Java
So the simplest way to do this is to 1) Define the documentation to explain valid combinations and
2) add the checks in the constructor
If a constructor throws an Exception than that is the responsibility of the invoker. Basically you would do something like this:
public MyClass(enum foo, enum bar, enum baz)
{
if(!validateCombination(foo,bar,baz))
{
throw new IllegalStateException("Contract violated");
}
}
private boolean validateCombination(enum foo, enum bar, enum baz)
{
//validation logic
}
Now this part is absolutely critical. Mark the class a final, it is possible that a partially constructed object can be recovered and abused to break your application. With a class marked as final a malicious program cannot extend the partially constructed object and wreak havoc.
One alternative idea is to write some automated tests to catch this, and hook them into your build process as a compulsory step before packaging/deploying your app.
If you think about what you're trying to catch here, it's code which is legal but wrong. While you could catch that during the compilation phase, this is exactly what tests are meant for.
This would fit your requirement of not being able to build any code with an illegal combination, because the build would still fail. And arguably it would be easier for other developers to understand than writing your own annotation processor...
The only way I know is to work with annotations.
Here is what I do I mean.
Now your constructor accepts 3 parameters:
public SomeClass(EnumType1 enum1,EnumType2 enum2, EnumType3 enum3){}
so you are calling it as following:
SomeClass obj = new SomeClass(EnumTupe1.VALUE1, EnumTupe2.VALUE2, EnumTupe1.VALUE3)
Change the constructor to be private. Create public constructor that accept 1 parameter of any type you want. It may be just a fake parameter.
public SomeClass(Placeholder p)
Now you have to require to call this constructor while each argument is annotated with special annotation. Let's call it TypeAnnotation:
SomeClass obj = new SomeClass(TypeAnnotation(
type1=EnumType1.VALUE1,
type2=EnumTupe2.VALUE2,
type3=EnumTupe1.VALUE3)
p3);
The call is more verbose but this is what we have to pay for compile time validation.
Now, how to define the annotation?
#Documented
#Retention({RetentionPolicy.RUNTIME, RetentionPolicy.SOURCE})
#Target(PARAMETER)
#interface TypeAnnotation {
EnumType1 type1();
EnumType2 type3();
EnumType3 type3();
}
Please pay attention that target is PARAMETER and retention values are RUNTIME and SOURCE.
RUNTIME allows reading this annotation at runtime, while SOURCE allows creating annotation processor that can validate the parameters at runtime.
Now the public constructor will call the 3-parameters private construcor:
public SomeClass(Placeholder p) {
this(readAnnotation(EnumType1.class), readAnnotation(EnumType2.class), readAnnotation(EnumType3.class), )
}
I am not implementing readAnnotation() here: it should be static method that takes stack trace, goes 3 elements back (to caller of the public costructor) and parses annotation TypeAnnotation.
Now is the most interesting part. You have to implement annotation processor.
Take a look here for instructions and here for an example of annotation processor.
You will have to add usage of this annotation processor to your build script and (optionally) to your IDE. In this case you will get real compilation error when your compatibility rules are violated.
I believe that this solution looks too complicated but if you really need this you can do this. It may take a day or so. Good luck.
Well, I am not aware of a compile time check but I do not think it is possible because how can the compiler know which value will be passed to the constructor (In case the value of your enum variable is calculated in runtime (e.g. by an If clause) ?
This can only be validated on runtime by using a validator method as you implemented for the enum types.
Example :
If in your code you have something like this :
EnumType1 enumVal;
if (<some condition>) {
enumVal = EnumType2.VALUE_SIX;
} else {
enumVal = EnumType2.VALUE_ONE;
}
There is no way the compiler can know which of the values will be assigned to enumVal so it won't be able to verify what is passed to the constructor until the if block is evaluated (which can be done only in runtime)
I'm, playing with the Android framework and try to get my mind deeper into Java. For This I read about Javas Generics and the Reflection API, while I'm not understanding it really.
Because I'm a lazy Dev ;) I tried to write an 'Parcelable-Container' in which I can put ANY Object I wish to get it Parcelable without the need to implement this for every Object again using methods of Java Reflection.
I write a test method like these:
public <T> void writeClassInformations(T t){
Class c = t.getClass();
System.out.println("DeclaredFields: ");
for (Field f : c.getDeclaredFields()){
System.out.println(f.toGenericString());
}
System.out.println("Fields: ");
for (Field f: c.getFields()){
System.out.println(f.toGenericString());
}
}
How can I get every member even if they are Objects or private Superclass members?
And another Question: The output is like this:
public int hello.test.Testclass.myID
how I get the value of 'myID'?
ADD:
I'm running in serious problems now. The Interface of Parcelable.Creator forces me to write a statement like: public static final Parcelable.Creator CREATOR =
new Parcelable.Creator<ParcelableBox<?>>()
Can I use ? somehow? Normally I use a constructor like ParcelableBox(E object). While it seems to me that I can't use Object methods on ? I even cannot pass it into a class variable like
public ParcelableBox<?> createFromParcel(Parcel source){
...
return new ParcelableBox<?>();
}
or ? myClass to use Reflection on it. Is this the end of javas reflection power? How can I get Class of ?
Reflection should be used sparingly, or not at all if it can be avoided, and especially not as a way to hack around good design principles. That being said, it can also be useful in certain situations ...
getDeclaredFields can return all types of fields while getFields only returns fields marked public.
The reason your test returns the same thing is that you're using getDeclaredFields in both statements.
how I get the value of 'myID'
You can only do that by operating on an instance of a class. E.g.,
T t = ...
Field field = t.getClass().getDeclaredField("myID");
field.setAccessible(true);
String value = (String) field.get(t);