I have 3 different kinds of custom annotations. Assume those are Annotation1,Annotation2,Annotation3.
I was applied these 3 annotations to some of the fields in my class. Now I am extracting/getting all the fields which are assigned these 3 annotations. So for that I wrote a method like
public List<Field> getAnnotation1Fields(Annotation1 argAnnotation1){
// Code to extract those fields...
}
So for my 3 annotations I need to write 3 different methods like
public List<Field> getAnnotation2Fields(Annotation2 argAnnotation2){
// Code to extract those fields...
}
public List<Field> getAnnotation3Fields(Annotation3 argAnnotation3){
// Code to extract those fields...
}
In the above methods the Extraction logic is same but the parameter type is different (Argument). Here my question is how can I call these three methods on single annotation...? So that one common method can be called for any type of annotation (Including our custom annotations).
Use method generics - You can define a variable type parameter for methods, as well as classes, like so:
public <T> List<Field> getAnnotationFields(T argAnnotation) {
// Get fields with annotation type T
}
And then you can call it as easily as:
Annotation3 thingy = ...;
getAnnotationFields(thingy); // Extracts fields for Annotation3
public List<Field> getAnnotatedFields(java.lang.annotation.Annotation annotation)
or
public List<Field> getAnnotatedFields(Class<? extends Annotation> annotationType)
depending on what you have instance (first) or type (second).
You could do it with this simple method:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public static List<Field> getAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType) {
Field[] declaredFields = clazz.getDeclaredFields();
List<Field> annotatedFields = new LinkedList<>();
for(Field field : declaredFields) {
if(field.getAnnotation(annotationType) != null)
annotatedFields.add(field);
}
return annotatedFields;
}
Usage example:
getAnnotatedFields(TestClass.class, Deprecated.class);
I hope that’s closer to your needs:
static <A extends Annotation> Map<String,A> getField2Annotation(
Class<?> declaringClass, Class<A> annotationType) {
Field[] fields=declaringClass.getDeclaredFields();
Map<String, A> map=Collections.emptyMap();
for(Field f:fields) {
A anno=f.getAnnotation(annotationType);
if(anno!=null) {
if(map.isEmpty()) map=new HashMap<String, A>();
map.put(f.getName(), anno);
}
}
return map;
}
Then you can do something like this:
public class Example {
#Retention(RetentionPolicy.RUNTIME)
#interface Foo { String value() default "bar"; }
#Foo static String X;
#Foo("baz") String Y;
public static void main(String[] args) {
Map<String, Foo> map = getField2Annotation(Example.class, Foo.class);
for(Map.Entry<String,Foo> e:map.entrySet()) {
System.out.println(e.getKey()+": "+e.getValue().value());
}
}
}
Related
I need to use some way to get all fields that are annotated with a specific annotation. The annotation may be at the field or the getter (of a super class), like
public MyClass {
#MyAnnotation
String myName;
int myAge;
#MyAnnotation
int getMyAge() { return myAge; }
}
So I need Field[] getAllAnnotatedFields(MyClass.class, MyAnnotation.class).
I could write that method on my own, but I wonder, if there exists some util method. (I cannot found one in Apache commons, Guava or Google reflections).
This is my solution using Apache commons:
public static Collection<String> getPropertyNamesListWithAnnotation(Class<?> targetClass, Class<? extends Annotation> annotationClass) {
Set<String> fieldNamesWithAnnotation = FieldUtils.getFieldsListWithAnnotation(targetClass, annotationClass).stream().map(Field::getName).collect(Collectors.toSet());
fieldNamesWithAnnotation.addAll(MethodUtils.getMethodsListWithAnnotation(targetClass, annotationClass, true, false).stream()
.map(Method::getName)
.filter(LangHelper::isValidGetterOrSetter)
.map(name -> StringUtils.uncapitalize(RegExUtils.replaceFirst(name, "^(get|set|is)", "")))
.collect(Collectors.toSet()));
return fieldNamesWithAnnotation;
}
private static boolean isValidGetterOrSetter(String methodName) {
if (!StringUtils.startsWithAny(methodName, "get", "set", "is")) {
LOG.warn("Annotated method is no valid getter or setter: '{}' -> Ignoring", methodName);
return false;
}
return true;
}
I have a lot of different objects which are being mapped so I've written a bunch of static mapping methods and one giant switch-case method which takes the type (a built-in field) and then uses the specialized mapping function.
Example:
public static SomeOtherObject mapFrom(SomeObject someObject) {
//... mapping logic
return someOtherObjectInstance;
}
//... a bunch of those
// root/main mapper
public static SomeOtherObjectBase mapFrom(SomeObjectBase someObjectBase) {
switch(someObjectBase.getType()) {
case SOME_TYPE: return mapFrom((SomeObject)someObjectBase);
//...
}
}
I then thought that I could probably convert this to an enum where each enumeration would be a mapper and would be bound to the type, thus avoiding a switch-case... something like this:
public enum SomeObjectMappers {
SOME_TYPE_MAPPER(SOME_TYPE) {
#Override
SomeOtherObject mapFrom(SomeObject someObject) {
//... mapping logic
return someOtherObjectInstance;
}
},
//... a bunch of those
;
private final Type type;
//constructor, getters...
abstract <T extends SomeOtherObjectBase, U extends SomeObjectBase> T mapFrom(U obj);
public static SomeOtherObjectBase mapFrom(SomeObjectBase obj) {
return Arrays.stream(values())
.filter(v -> v.getType() == type)
.map(m -> m.mapFrom(obj))
.findFirst()
.orElse(null);
}
}
However this does not really compile/work as for some reason mapper implementation in SOME_TYPE_MAPPERdoes not accept concrete subclasses SomeOtherObject and SomeObject as valid signatures for the abstract method.
Can this not be done?
Suppose I have an interface like this;
interface Validator<T>{
void validate<T value>
}
And these implementations ;
class StringValidator implements Validator<String>{
void validate<String value>{}
}
class OrderValidator implements Validator<Order>{
void validate<Order value>{}
}
In ValidatorRegisterer class I have a map;
class ValidationRegisterer{
Map<String, Validator> validatorsForPath = new HashMap<String, Validator>();
public Map<String, Validator> registerers(){
return validatorsForPath;
}
public void register(String path, Validator validator){
validatorsForPath.put(path, validator);
}
}
What I want is to iterate over this map in ValidationManager class with type safety;
class ValidationManager<RootObject>{
List<ValidationRegisterer> validationRegisterers;
public ValidationManager(List<ValidationRegisterer> validationRegisterers){
this.validationRegisterers = validationRegisterers;
}
public void validate(RootObject object){
for(ValidationRegisterer validationRegisterer in validationRegisterers){
for(String path : validationRegisterer.keySet()){
Object value = object.getPath(path);
Validator validator = validationRegisterer.get(path);
validator.validate(value);
//this line gets unchecked call to validate(T) warning and I want to get rid of it
//the problem is validationRegisterers map can contain both StringValidator and OrderValidator,
//so the value can be a String or an Order
//do I have to cast the value to the type of validator's T type?
}
}
}
Map<String, Validator> validatorsForPath = new HashMap<String, Validator>();
}
I tried to explain the situation in the last code sample comments.
Declare as follows to remove warnings :
Validator<Object> validator = validationRegisterer.get(path);
In this case you are declaring the validator reference that would work on Object type.
later you can typecast to Order or String after doing an instanceof test.
You need to make ValidationRegisterer class generic like this:
class ValidationRegisterer<T extends Validator> {
Map<String, T> validatorsForPath = new HashMap<String, T>();
public Map<String, T> registerers(){
return validatorsForPath;
}
public void register(String path, T validator){
validatorsForPath.put(path, validator);
}
}
And then maintain separate lists for these two types of ValidationRegisterer
class ValidationManager {
List<ValidationRegisterer<StringValidator>> strValidationRegisterers;
List<ValidationRegisterer<OrderValidator>> ordValidationRegisterers;
....
}
I will assume that with "type safety" you mean that you want to be certain that the object returned for a certain path is really of the type that the associated Validator accepts.
One problem is that the type parameter for the Validator is not available at compile time since, as you say yourself, any kind of Validator can be in the map.
Also, object.getPath(path) will always return an Object which will always need casting at runtime, so the fact that the validate method limits its argument to type T is of little use.
So the best you can do is make validation fail fast in case the object is not of the correct type.
A solution would be to
1. store the Class object for the Validator,
2. let validate accept an Object as parameter and dynamically cast the object to the validator type at the beginning of the validate method. This can be done in an abstract base class.
Example:
interface Validator<T> {
void validate(Object value);
Class<T> getType();
}
abstract class BaseValidator<T> implements Validator<T> {
private final Class<T> type;
public BaseValidator(Class<T> type) {
this.type = type;
}
public final void validate(Object o) {
doValidate(type.cast(o)); // wrong type will fail fast here
}
public final Class<T> getType() {
return type;
}
protected abstract void doValidate(T value);
}
class StringValidator extends BaseValidator<String> {
public StringValidator() {
super(String.class);
}
protected void doValidate(String value) {
// do actual string validation here
}
}
An alternative solution if you want to keep Object out of the Validator interface would be to let the path be resolved by a type parameterized object that has a reference the validator and performs the dynamic cast, and keep that in your registry map as value:
interface Validator<T> {
void validate(final T value);
}
class PathValidator<T> {
private final Class<T> type;
private final Validator<T> validator;
public PathValidator(final Class<T> type, final Validator<T> validator) {
this.type = type;
this.validator = validator;
}
public void validate(final RootObject object, final String path) {
T value = type.cast(object.getPath(path)); // throws ClassCastException here if not the correct type
validator.validate(value);
}
}
You would then have a Map<String, PathValidator<?> in your ValidationRegisterer class.
I'd personally prefer this alternative solution.
The problem: I've a Function Object interface defined in a class:
public static interface FunctionObject<T> {
void process(T object);
}
I need it generic because I'd like to use T methods in the process implementations.
Then, in other generic class, I've a Map where I have classes as keys and function objects as values:
Map<Class<T>, FunctionObject<T>> map;
But I also want the map to accept subtype classes and function objects of supertypes OF THE KEY TYPE, so I did this:
Map<Class<? extends T>, FunctionObject<? super T>> map; //not what I need
The basic idea is to be able to use the map as follows:
//if T were Number, this should be legal
map.put(Class<Integer>, new FunctionObject<Integer>(){...});
map.put(Class<Float>, new FunctionObject<Number>(){...});
map.put(Class<Double>, new FunctionObject<Object>(){...});
As I want to enforce the FunctionObject has the type of the class key or a supertype, what I really would like to define is this:
Map<Class<E extends T>, FunctionObject<? super E>>> map;
How can I achieve the desired effect? Is a typesafe heterogenous container the only option? What would the Map generic types look like to allow populating it from a reference?
Parametrized container, seems to work just fine:
public class MyMap<T>
{
interface FunctionObject<X> {}
private Map<Class<? extends T>, FunctionObject<Object>> map = new HashMap<>();
#SuppressWarnings("unchecked")
public <E extends T> void put(Class<E> c, FunctionObject<? super E> f)
{
map.put(c, (FunctionObject<Object>) f);
}
public <E extends T> FunctionObject<Object> get(Class<E> c)
{
return map.get(c);
}
public static void Main(String[] args)
{
MyMap<Number> map = new MyMap<>();
map.put(Integer.class, new FunctionObject<Integer>() {});
map.put(Float.class, new FunctionObject<Number>() {});
map.put(Double.class, new FunctionObject<Object>() {});
}
}
Edited to comply to the question. Sadly there is no way to avoid the downcasting to object.
Edit added get().
You can do this with encapsulation, assuming you only use the map through the method which check this on a per entry basis.
The following add method avoids the need to double up on the type as well.
public class Main {
interface FunctionObject<T> { }
private final Map<Class, FunctionObject> map = new LinkedHashMap<Class, FunctionObject>();
public <T> void add(FunctionObject<T> functionObject) {
Class<T> tClass = null;
for (Type iType : functionObject.getClass().getGenericInterfaces()) {
ParameterizedType pt = (ParameterizedType) iType;
if (!pt.getRawType().equals(FunctionObject.class)) continue;
Type t = pt.getActualTypeArguments()[0];
tClass = (Class<T>) t;
break;
}
map.put(tClass, functionObject);
}
public <T> void put(Class<T> tClass, FunctionObject<T> functionObject) {
map.put(tClass, functionObject);
}
public <T> FunctionObject<T> get(Class<T> tClass) {
return map.get(tClass);
}
public static void main(String... args) throws IOException {
Main m = new Main();
m.add(new FunctionObject<Integer>() {
});
FunctionObject<Integer> foi = m.get(Integer.class);
System.out.println(foi.getClass().getGenericInterfaces()[0]);
}
}
prints
Main.Main$FunctionObject<java.lang.Integer>
You can use #SuppressWarnings("unchecked") if you want to disable the warning.
The point is; there is no way to describe the constraint you have in the field declaration, you can achieve the same result if you use accessor methods which do the check on a per entry basis. You can add runtime checks as well if you need to ensure raw types are correct.
I've created an inherited field type annotation that is placed on private properties in an abstract superclass.
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Lifecycle{
Type type();
String name() default "";
public enum Type{
DISCRIMINATOR,INITIAL,MUTABLE
}
}
I'm calling a method from the subclass which seeks to gather all the annotations of this type (inherited or otherwise) and return them in a list.
public static <T extends Annotation> List<T> getAnnotation(final Class c, final Class<T> ann) {
return getAnnotation(c, ann, new ArrayList<T>());
}
public static <T extends Annotation> List<T> getAnnotation(final Class c, final Class<T> ann, List<T> aList) {
Field[] fields = c.getFields();
for (int i = 0; i < fields.length; i++) {
Field myField = fields[i];
myField.setAccessible(true);
T found = myField.getAnnotation(ann);
if (found != null) {
aList.add(found);
}
}
if (!c.getSuperclass().equals(Object.class)) {
return getAnnotation(c.getSuperclass(), ann, aList);
} else {
return aList;
}
}
For some reason unbeknownst to me this doesn't work. All fields both inherited and not are definitely found. Likewise all classes in the inheritance structure are traversed, but for some reason, myField.getAnnotation(ann); is always null.
I'm kind of at a loss - I don't understand why if I can properly retrieve the field (as well as get and set it's value, e.g. not a security thing) that I can't see it's annotation.
Instead of this one:
Field[] fields = c.getFields();
try this:
Field[] fields = c.getDeclaredFields();
If this doesn't work, try to iterate over declared annotations:
for(Annotation annotation : field[i].getDeclaredAnnotations()) {
...
}