I jave a simple Java bean with 4 attributes, getter/setter, and some overided methods like toString, equals and hashCode.
Above every Attribute is a custom Annotation:
import java.lang.annotation.*;
import java.lang.annotation.RetentionPolicy;
#Target(ElementType.FIELD)
#Retention( RetentionPolicy.RUNTIME )
public #interface DAOProperty {
String name();
String type();
boolean identifier() default false;
}
/** The id. */
#DAOProperty(name = "id", type = "long", identifier = true)
private long id;
If I pass the bean.class to another method
generateEntity(User.class);
...
private static MEntity generateEntity(Class<?> bean) {...}
and debug it, it seems to bee empty, except for the class name. All arrays like methods, annotations and fields are of zero size.
Where did I go wrong?
Use beanClass.getDeclaredFields() instead of getFields(). Then iterate the array and for each Field call getAnnotations()
getFields() (and the similar methods) return only the public members.
Anyway, why don't you use JPA, instead of creating your own annotations and annotation processors?
Don't look at the internal fields of the class. They aren't relevant for you. The only thing that should interest you are the return values of the methods. It's very likely that java.lang.Class uses those fields to store information that is created on-demand.
In that case looking at the fields of the Class object at runtime won't tell you the right values (at least not always). Inspecting the return values of the desired methods, however should give the right values.
Related
Is there any java annotation for adding extra information to the filed of a class?
I need something like the following example to be accessible at run-time by reflections:
public class A
{
#Description("my field")
public String name = "";
}
It's not hard to write something like this yourself. For example:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Description {
String value() default "";
}
Then to use it:
public class MyClass {
#Description("my field")
private String name = "";
}
And to get the value:
Field nameField = MyClass.class.getDeclaredField("name");
Description d = nameField.getAnnotation(Description.class);
System.out.println(d.value()); // Prints "my field"
Key pieces:
Note that value is a special attribute. It doesn't require you to specify the name. If you didn't use value, you'd have to do something more like #Description(desc = "my field") and reference the name of it directly.
default specifies the default value. It doesn't make much sense here, but it's useful to know. This means you could write #Description without a value specified and this would be legal. Calling value() will return the empty string in this case since that's the default value. If you don't specify a default, then you're required to provide a value.
#Target is used to specify what kind of element(s) the annotation can be used on. In this case, we specified only FIELD.
#Retention specifies how long it's kept around and whether or not it can be accessed. SOURCE means that it's lost after compiling. This is useful when you only need it for annotation processing. CLASS means that it's stored in the bytecode, but you can't access it through reflection. RUNTIME is the same as CLASS, except you can access it through reflection.
I noticed that if I create an annotation:
public #interface NullableTypeOverride {
NullableType hibernateTypeOverride();
}
I have limited options for annotation attributes. The above code will not work because annotations only take primitive, String or Class types for their attributes.
So in this case I can't use this annotation like this:
#NullableTypeOverride(hibernateTypeOverride = Hibernate.INTEGER)
private Long distance;
My guess is that it has something to do with compile time vs. runtime but I'm not entirely sure. So what is the reason for this limitation and how can I work around it?
The JLS states
It is a compile-time error if the return type of a method declared in
an annotation type is not one of the following: a primitive type,
String, Class, any parameterized invocation of Class, an enum type
(§8.9), an annotation type, or an array type (§10) whose element type
is one of the preceding types.
The reason for this is that annotations must have a constant value. If you provide a reference to an object that may change, you'll have problems. This is only relevant if the annotation's Retention is RUNTIME.
public class Person {
public String name;
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
Person person();
}
#MyAnnotation(person = ???) // how to guarantee it won't change at runtime?
public void method1() {...}
What's that value supposed to be? And how can reflection libs cache it?
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
annotation.person(); // should be the same value every time
Remember, annotations are supposed to be metadata.
How does annotation work with Java? And how can I create custom annotations like this:
#Entity(keyspace=':')
class Student
{
#Id
#Attribute(value="uid")
Long Id;
#Attribute(value="fname")
String firstname;
#Attribute(value="sname")
String surname;
// Getters and setters
}
Basically, what I need to have is this POJO be serialized like this when persisted:
dao.persist(new Student(0, "john", "smith"));
dao.persist(new Student(1, "katy", "perry"));
Such that, the actual generated/persisted object is a Map<String,String> like this:
uid:0:fname -> john
uid:0:sname -> smith
uid:1:fname -> katy
uid:1:sname -> perry
Any ideas how to implement this?
If you create custom annotations you will have to use Reflection API Example Here to process them.
You can refere How to declare annotation.
Here is how example annotation declaration in java looks like.
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Test { }
Retention and Target are known as meta-annotations.
RetentionPolicy.RUNTIME indicates that you want to retain the annotation at runtime and you can access it at runtime.
ElementType.METHOD indicates that you can declare annotation only on methods similarly you can configure your annotation for class level, member variable level etc.
Each Reflection class has methods to get annotations which are declared.
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
Returns this element's annotation for the specified type if such an annotation is present, else null.
public Annotation[] getDeclaredAnnotations()
Returns all annotations that are directly present on this element. Unlike the other methods in this interface, this method ignores inherited annotations. (Returns an array of length zero if no annotations are directly present on this element.) The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.
You will find these methods present for Field, Method,Class classes.
e.g.To retrieve annotations present on specified class at run time
Annotation[] annos = ob.getClass().getAnnotations();
Is there some way of using magic methods in Java like there is in PHP with __call?
For instance:
class foo {
#Setter #Getter
int id;
#Getter
Map <String, ClassInFoo> myMap;
protected class ClassInFoo {
#Setter #Getter
String name;
}
#Setter
String defaultKey;
}
I'm using Project Lombok annotations for getter and setter methods to simplify the code.
Let's consider that that my map contains several items mapped by String and the defaultKey defines the default one.
What I would like is to be able to call foo.getName() which would return the default name as foo.myMap.get(defaultKey).getName().
The reason I can't just write all the getters manually is that the Foo class is in fact inherited with generics and the the inner class might be different.
I sort of need something like:
function Object __call(method) {
if (exist_method(this.method)
return this.method();
else
return this.myMap.get(defaultKey).method();
}
Is this somehow possible in Java?
EDIT:
I made a more precise example of what I am trying to achieve here: https://gist.github.com/1864457
The only reason of doing this is to "shorthand" the methods in the inner class.
You absolutely can through reflection by using its features like
public Method getMethod(String name, Class<?>... parameterTypes)
that can be used to see if a class has some methods defined but I don't see how your problem couldn't be solved with a proper use of interfaces, inheritance and overriding of methods
Features like reflection are provided to manage certain, otherwise unsolvable, issues but Java is not PHP so you should try to avoid using it when possible, since it's not in the philosophy of the language.
Isn't it the whole point of inheritance and overriding?
Base class:
public Object foo() {
return this.myMap.get(defaultKey).method();
}
Subclass:
#Overrides
public Object foo() {
return whateverIWant;
}
My question in short: how do I detect if a java annotation is present (and in the right place) for a given user class/object.
Details of the "problem"
Lets say I have two java classes:
public class Line {
private List<Cell> cells;
public Line(Object... annotatedObjects) {
// check if annotations #Line and #Cell are present in annotatedObjects.
}
// getter/setter for cells.
}
public class Cell {
// some members
// some methods
}
A Line object holds Cells.
I also have two annotations, like:
public #interface Line {
// some stuff here
}
public #interface Cell {
// some stuff here
}
I also have a bunch of user classes (two will do for this example) that contain the #Line and #Cell annotations I specified, like:
#Line(name="pqr", schema="three")
public class AUserClass {
#Cell
private String aString;
}
#Line(name="xyz", schema="four")
public class AnotherUserClass {
#Cell(name="birthday")
private Date aDate;
}
The problem: When I instantiate a new Line object, I want to be able to pass the user classes/objects into the Line constructor. The Line constructor then finds out if the passed user classes/objects are valid classes that can be processed. Only user classes that have a #Line annotation for the class, and at least one #Cell annotation for its members are valid objects that can be passed into the constructor of the Line object.
All other passed objects are invalid. The moment a valid user object is passed, all the available members that are tagged as #Cell in that object are transformed to Cell objects and added to the cells list.
My questions:
is this possible to detect the annotations in this object/class at runtime, and only for THIS passed object (I don't want to scan for annotations on the classpath!)?
is it possible to detect the datatype of the #Cell tagged members? This is needed because the Cell class doesn't accept all datatypes.
is it possible to retrieve the actual member name (specified in the java file) so that the user doesn't have to specify the members Cell name. I want the user to be able to write #Cell (without a name) and #Cell(name="aName"), and when only #Cell is specified, the name of the member is used instead. I have no idea if this information is still available at runtime using reflection.
How to detect if the annotations are in the right place?If code is tagged like this, then the object should be ignored (or maybe an exception is thrown)?
#Cell // oh oh, that's no good :(
public class WrongClass {
// some members
}
Could you provide some startup code, so I know a little to get going with this problem. I am really new to annotations and reflection. BTW: I am using the latest jvm 1.6+
Thank you for your kind help!
First you need to have retention policy on your annotations so you can read them with reflection
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public static #interface Line {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public static #interface Cell {
}
Second you need to test if the class has the Line annotation with isAnnotationPresent(annotationClass). This method is accessible from java.lang.Class and a java.lang.reflect.Field.
NOTE: that you need to retrieve the fields that are private with class.getDeclaredField(fieldName).
3. I don't think you can make an annotation have a default value based on a propertyName but you can make name optional by providing a default String name() default DEFAULT and check for that value when iterating through the fields and either use the value stored in name() or the propertyName
Q.1 :is this possible to detect the annotations in this object/class at runtime, and only for THIS passed object (I don't want to scan for annotations on the classpath!)?
Yes it is very well possible using isAnnotationPresent
#Deprecated
public class AnnotationDetection {
public static void main(String[] args) {
AnnotationDetection annotationDetection = new AnnotationDetection();
System.out.println(annotationDetection.getClass().isAnnotationPresent(Deprecated.class));
}
}
Note that annotation which are scoped to retain at Runtime will be available only,