Skip to content

Instantly share code, notes, and snippets.

@tomaszalusky
Created April 6, 2015 22:09
Show Gist options
  • Save tomaszalusky/dcb695c5c3ae6d49b913 to your computer and use it in GitHub Desktop.
Save tomaszalusky/dcb695c5c3ae6d49b913 to your computer and use it in GitHub Desktop.
package anno;
import static java.util.stream.Collectors.toList;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedArrayType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.stream.Stream;
/**
* This example shows the placement of type annotation on array types
* so as to have meaning equivalent to collections types.
* While annotations on collection types are intuitively read from left to right,
* annotations on arrays have different order due to syntax of array.
*/
public abstract class TypeAnnotations {
// -------------------------------------------------------------------------
// Nullable* is just for demonstration purpose, no null check is point of this example.
// Different Nullable* classes help to distinguish particular level of type nesting.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE,ElementType.PARAMETER})
public static @interface NullableC {} // nullable container (array or collection)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE,ElementType.PARAMETER})
public static @interface Nullable1 {} // nullable thing in container
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE,ElementType.PARAMETER})
public static @interface Nullable2 {} // nullable thing in thing in container
// Name convention: l = List, a = array, n = Nullable, s = String
// Every two fields in following pairs are equivalent according to type annotations (e.g. the only difference is array vs. collection).
// -----------FIELDS--------------------------------------------------------
List<@Nullable1 String> lns;
@Nullable1 String[] ans;
@NullableC List<String> nls;
String @NullableC [] nas;
@NullableC List<@Nullable1 String> nlns;
@Nullable1 String @NullableC [] nans; // note: annotation for deepest level is at the beginning
@NullableC List<@Nullable1 List<@Nullable2 String>> nlnlns;
@Nullable2 String @NullableC [] @Nullable1 [] nanans; // note: annotations for other levels are in front of [] and go from left to right
private static void testFields() throws Exception {
Field lns = field("lns");
AnnotatedParameterizedType lns_t = (AnnotatedParameterizedType)lns.getAnnotatedType();
System.out.println(at2s(lns_t) + " List of " + at2s(lns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Field ans = field("ans");
AnnotatedArrayType ans_t = (AnnotatedArrayType)ans.getAnnotatedType();
System.out.println(at2s(ans_t) + " array of " + at2s(ans_t.getAnnotatedGenericComponentType()) + " Strings");
Field nls = field("nls");
AnnotatedParameterizedType nls_t = (AnnotatedParameterizedType)nls.getAnnotatedType();
System.out.println(at2s(nls_t) + " List of " + at2s(nls_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Field nas = field("nas");
AnnotatedArrayType nas_t = (AnnotatedArrayType)nas.getAnnotatedType();
System.out.println(at2s(nas_t) + " array of " + at2s(nas_t.getAnnotatedGenericComponentType()) + " Strings");
Field nlns = field("nlns");
AnnotatedParameterizedType nlns_t = (AnnotatedParameterizedType)nlns.getAnnotatedType();
System.out.println(at2s(nlns_t) + " List of " + at2s(nlns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Field nans = field("nans");
AnnotatedArrayType nans_t = (AnnotatedArrayType)nans.getAnnotatedType();
System.out.println(at2s(nans_t) + " array of " + at2s(nans_t.getAnnotatedGenericComponentType()) + " Strings");
Field nlnlns = field("nlnlns");
AnnotatedParameterizedType nlnlns_t = (AnnotatedParameterizedType)nlnlns.getAnnotatedType();
AnnotatedParameterizedType nlnlns_tt = (AnnotatedParameterizedType)nlnlns_t.getAnnotatedActualTypeArguments()[0];
System.out.println(at2s(nlnlns_t) + " List of " + at2s(nlnlns_tt) + " List of " + at2s(nlnlns_tt.getAnnotatedActualTypeArguments()[0]) + " Strings");
Field nanans = field("nanans");
AnnotatedArrayType nanans_t = (AnnotatedArrayType)nanans.getAnnotatedType();
AnnotatedArrayType nanans_tt = (AnnotatedArrayType)nanans_t.getAnnotatedGenericComponentType();
System.out.println(at2s(nanans_t) + " array of " + at2s(nanans_tt) + " array of " + at2s(nanans_tt.getAnnotatedGenericComponentType()) + " Strings");
}
static Field field(String name) throws Exception {
Field result = TypeAnnotations.class.getDeclaredField(name);
System.out.printf("field %-6s has annotations %-11s and type ", name, a2s(result));
return result;
}
// -----------PARAMETERS----------------------------------------------------
abstract void m(
List<@Nullable1 String> lns,
@Nullable1 String[] ans,
@NullableC List<String> nls,
String @NullableC [] nas,
@NullableC List<@Nullable1 String> nlns,
@Nullable1 String @NullableC [] nans,
@NullableC List<@Nullable1 List<@Nullable2 String>> nlnlns,
@Nullable2 String @NullableC [] @Nullable1 [] nanans
);
static final Method m = Stream.of(TypeAnnotations.class.getDeclaredMethods())
.filter(mm -> "m".equals(mm.getName())).findFirst().get(); // I'm just lazy to enumerate all raw types and handle exception
static void testParameters() throws Exception {
Parameter lns = parameter("lns");
AnnotatedParameterizedType lns_t = (AnnotatedParameterizedType)lns.getAnnotatedType();
System.out.println(at2s(lns_t) + " List of " + at2s(lns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Parameter ans = parameter("ans");
AnnotatedArrayType ans_t = (AnnotatedArrayType)ans.getAnnotatedType();
System.out.println(at2s(ans_t) + " array of " + at2s(ans_t.getAnnotatedGenericComponentType()) + " Strings");
Parameter nls = parameter("nls");
AnnotatedParameterizedType nls_t = (AnnotatedParameterizedType)nls.getAnnotatedType();
System.out.println(at2s(nls_t) + " List of " + at2s(nls_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Parameter nas = parameter("nas");
AnnotatedArrayType nas_t = (AnnotatedArrayType)nas.getAnnotatedType();
System.out.println(at2s(nas_t) + " array of " + at2s(nas_t.getAnnotatedGenericComponentType()) + " Strings");
Parameter nlns = parameter("nlns");
AnnotatedParameterizedType nlns_t = (AnnotatedParameterizedType)nlns.getAnnotatedType();
System.out.println(at2s(nlns_t) + " List of " + at2s(nlns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Parameter nans = parameter("nans");
AnnotatedArrayType nans_t = (AnnotatedArrayType)nans.getAnnotatedType();
System.out.println(at2s(nans_t) + " array of " + at2s(nans_t.getAnnotatedGenericComponentType()) + " Strings");
Parameter nlnlns = parameter("nlnlns");
AnnotatedParameterizedType nlnlns_t = (AnnotatedParameterizedType)nlnlns.getAnnotatedType();
AnnotatedParameterizedType nlnlns_tt = (AnnotatedParameterizedType)nlnlns_t.getAnnotatedActualTypeArguments()[0];
System.out.println(at2s(nlnlns_t) + " List of " + at2s(nlnlns_tt) + " List of " + at2s(nlnlns_tt.getAnnotatedActualTypeArguments()[0]) + " Strings");
Parameter nanans = parameter("nanans");
AnnotatedArrayType nanans_t = (AnnotatedArrayType)nanans.getAnnotatedType();
AnnotatedArrayType nanans_tt = (AnnotatedArrayType)nanans_t.getAnnotatedGenericComponentType();
System.out.println(at2s(nanans_t) + " array of " + at2s(nanans_tt) + " array of " + at2s(nanans_tt.getAnnotatedGenericComponentType()) + " Strings");
}
static Parameter parameter(String name) throws Exception {
Parameter result = Stream.of(m.getParameters()).filter(p -> p.getName().equals(name)).findFirst().get();
System.out.printf("parameter %-6s has annotations %-11s and type ", name, a2s(result));
return result;
}
// -----------METHODS-------------------------------------------------------
abstract List<@Nullable1 String> m_lns();
abstract @Nullable1 String[] m_ans();
abstract @NullableC List<String> m_nls();
abstract String @NullableC [] m_nas();
abstract @NullableC List<@Nullable1 String> m_nlns();
abstract @Nullable1 String @NullableC [] m_nans();
abstract @NullableC List<@Nullable1 List<@Nullable2 String>> m_nlnlns();
abstract @Nullable2 String @NullableC [] @Nullable1 [] m_nanans();
static void testMethods() throws Exception {
Method lns = method("lns");
AnnotatedParameterizedType lns_t = (AnnotatedParameterizedType)lns.getAnnotatedReturnType();
System.out.println(at2s(lns_t) + " List of " + at2s(lns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Method ans = method("ans");
AnnotatedArrayType ans_t = (AnnotatedArrayType)ans.getAnnotatedReturnType();
System.out.println(at2s(ans_t) + " array of " + at2s(ans_t.getAnnotatedGenericComponentType()) + " Strings");
Method nls = method("nls");
AnnotatedParameterizedType nls_t = (AnnotatedParameterizedType)nls.getAnnotatedReturnType();
System.out.println(at2s(nls_t) + " List of " + at2s(nls_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Method nas = method("nas");
AnnotatedArrayType nas_t = (AnnotatedArrayType)nas.getAnnotatedReturnType();
System.out.println(at2s(nas_t) + " array of " + at2s(nas_t.getAnnotatedGenericComponentType()) + " Strings");
Method nlns = method("nlns");
AnnotatedParameterizedType nlns_t = (AnnotatedParameterizedType)nlns.getAnnotatedReturnType();
System.out.println(at2s(nlns_t) + " List of " + at2s(nlns_t.getAnnotatedActualTypeArguments()[0]) + " Strings");
Method nans = method("nans");
AnnotatedArrayType nans_t = (AnnotatedArrayType)nans.getAnnotatedReturnType();
System.out.println(at2s(nans_t) + " array of " + at2s(nans_t.getAnnotatedGenericComponentType()) + " Strings");
Method nlnlns = method("nlnlns");
AnnotatedParameterizedType nlnlns_t = (AnnotatedParameterizedType)nlnlns.getAnnotatedReturnType();
AnnotatedParameterizedType nlnlns_tt = (AnnotatedParameterizedType)nlnlns_t.getAnnotatedActualTypeArguments()[0];
System.out.println(at2s(nlnlns_t) + " List of " + at2s(nlnlns_tt) + " List of " + at2s(nlnlns_tt.getAnnotatedActualTypeArguments()[0]) + " Strings");
Method nanans = method("nanans");
AnnotatedArrayType nanans_t = (AnnotatedArrayType)nanans.getAnnotatedReturnType();
AnnotatedArrayType nanans_tt = (AnnotatedArrayType)nanans_t.getAnnotatedGenericComponentType();
System.out.println(at2s(nanans_t) + " array of " + at2s(nanans_tt) + " array of " + at2s(nanans_tt.getAnnotatedGenericComponentType()) + " Strings");
}
static Method method(String name) throws Exception {
Method result = TypeAnnotations.class.getDeclaredMethod("m_" + name);
System.out.printf("method %-6s has annotations %-11s and type ", name, a2s(result));
return result;
}
public static void main(String[] args) throws Exception {
System.out.printf("----------------------------------------------------%n");
testFields();
System.out.printf("----------------------------------------------------%n");
testParameters();
System.out.printf("----------------------------------------------------%n");
testMethods();
}
/**
* Dumps all simple class names of annotations of given annotated Java construct into String.
* @param o
* @return
*/
static String a2s(AnnotatedElement o) {
String result = Stream.of(o.getAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(toList()).toString();
return result;
}
/**
* Dumps first annotation of given type (if any) to String.
* @param type
* @return
*/
static String at2s(AnnotatedType type) {
return type.getAnnotations().length == 0 ? "annotationless" : type.getAnnotations()[0].annotationType().getSimpleName();
}
}
/* OUTPUT:
----------------------------------------------------
field lns has annotations [] and type annotationless List of Nullable1 Strings
field ans has annotations [Nullable1] and type annotationless array of Nullable1 Strings
field nls has annotations [NullableC] and type NullableC List of annotationless Strings
field nas has annotations [] and type NullableC array of annotationless Strings
field nlns has annotations [NullableC] and type NullableC List of Nullable1 Strings
field nans has annotations [Nullable1] and type NullableC array of Nullable1 Strings
field nlnlns has annotations [NullableC] and type NullableC List of Nullable1 List of Nullable2 Strings
field nanans has annotations [Nullable2] and type NullableC array of Nullable1 array of Nullable2 Strings
----------------------------------------------------
parameter lns has annotations [] and type annotationless List of Nullable1 Strings
parameter ans has annotations [Nullable1] and type annotationless array of Nullable1 Strings
parameter nls has annotations [NullableC] and type NullableC List of annotationless Strings
parameter nas has annotations [] and type NullableC array of annotationless Strings
parameter nlns has annotations [NullableC] and type NullableC List of Nullable1 Strings
parameter nans has annotations [Nullable1] and type NullableC array of Nullable1 Strings
parameter nlnlns has annotations [NullableC] and type NullableC List of Nullable1 List of Nullable2 Strings
parameter nanans has annotations [Nullable2] and type NullableC array of Nullable1 array of Nullable2 Strings
----------------------------------------------------
method lns has annotations [] and type annotationless List of Nullable1 Strings
method ans has annotations [Nullable1] and type annotationless array of Nullable1 Strings
method nls has annotations [NullableC] and type NullableC List of annotationless Strings
method nas has annotations [] and type NullableC array of annotationless Strings
method nlns has annotations [NullableC] and type NullableC List of Nullable1 Strings
method nans has annotations [Nullable1] and type NullableC array of Nullable1 Strings
method nlnlns has annotations [NullableC] and type NullableC List of Nullable1 List of Nullable2 Strings
method nanans has annotations [Nullable2] and type NullableC array of Nullable1 array of Nullable2 Strings
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment