Infer AnnotationAttributes method return types
- Drop 'expectedType' parameter from #getClass and #getEnum methods and
rely on compiler inference based on type of assigned variable, e.g.
public @interface Example {
Color color();
Class<? extends UserType> userType();
int order() default 0;
}
AnnotationAttributes example =
AnnotationUtils.getAnnotationAttributes(Example.class, true, true);
Color color = example.getEnum("color");
Class<? extends UserType> userType = example.getClass("userType");
or in cases where there is no variable assignment (and thus no
inference possible), use explicit generic type, e.g.
bean.setColor(example.<Color>getEnum("color"));
- Rename #get{Int=>Number} and update return type from int to
<N extends Number>, allowing invocations such as:
int order = example.getNumber("order");
These changes reduce the overall number of methods exposed by
AnnotationAttributes, while at the same time providing comprehensive
access to all possible annotation attribute types -- that is, instead of
requiring explicit #getInt, #getFloat, #getDouble methods, the
single #getNumber method is capabable of handling them all, and without
any casting required. And the obvious additional benefit is more concise
invocation as no redundant 'expectedType' parameters are required.
This commit is contained in:
@@ -92,16 +92,18 @@ public class AnnotationAttributes extends LinkedHashMap<String, Object> {
|
||||
return doGet(attributeName, Boolean.class);
|
||||
}
|
||||
|
||||
public int getInt(String attributeName) {
|
||||
return doGet(attributeName, Integer.class);
|
||||
}
|
||||
|
||||
public <E extends Enum<?>> E getEnum(String attributeName, Class<E> enumType) {
|
||||
return doGet(attributeName, enumType);
|
||||
@SuppressWarnings("unchecked")
|
||||
public <N extends Number> N getNumber(String attributeName) {
|
||||
return (N) doGet(attributeName, Integer.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Class<? extends T> getClass(String attributeName, Class<T> expectedType) {
|
||||
public <E extends Enum<?>> E getEnum(String attributeName) {
|
||||
return (E) doGet(attributeName, Enum.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Class<? extends T> getClass(String attributeName) {
|
||||
return (Class<T>)doGet(attributeName, Class.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@ public class AnnotationAttributesTests {
|
||||
assertThat(a.getStringArray("names"), equalTo(new String[] { "dave", "frank", "hal" }));
|
||||
assertThat(a.getBoolean("bool1"), equalTo(true));
|
||||
assertThat(a.getBoolean("bool2"), equalTo(false));
|
||||
assertThat(a.getEnum("color", Color.class), equalTo(Color.RED));
|
||||
assertTrue(a.getClass("clazz", Number.class).equals(Integer.class));
|
||||
assertThat(a.<Color>getEnum("color"), equalTo(Color.RED));
|
||||
assertTrue(a.getClass("clazz").equals(Integer.class));
|
||||
assertThat(a.getClassArray("classes"), equalTo(new Class[] { Number.class, Short.class, Integer.class }));
|
||||
assertThat(a.getInt("number"), equalTo(42));
|
||||
assertThat(a.getAnnotation("anno").getInt("value"), equalTo(10));
|
||||
assertThat(a.<Integer>getNumber("number"), equalTo(42));
|
||||
assertThat(a.getAnnotation("anno").<Integer>getNumber("value"), equalTo(10));
|
||||
assertThat(a.getAnnotationArray("annoArray")[0].getString("name"), equalTo("algernon"));
|
||||
}
|
||||
|
||||
@@ -68,13 +68,13 @@ public class AnnotationAttributesTests {
|
||||
AnnotationAttributes a = new AnnotationAttributes();
|
||||
a.put("color", "RED");
|
||||
try {
|
||||
a.getEnum("", Color.class);
|
||||
a.getEnum("");
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
|
||||
}
|
||||
try {
|
||||
a.getEnum(null, Color.class);
|
||||
a.getEnum(null);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(), equalTo("attributeName must not be null or empty"));
|
||||
@@ -86,7 +86,7 @@ public class AnnotationAttributesTests {
|
||||
AnnotationAttributes a = new AnnotationAttributes();
|
||||
a.put("color", "RED");
|
||||
try {
|
||||
a.getEnum("colour", Color.class);
|
||||
a.getEnum("colour");
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(), equalTo("Attribute 'colour' not found"));
|
||||
@@ -98,11 +98,11 @@ public class AnnotationAttributesTests {
|
||||
AnnotationAttributes a = new AnnotationAttributes();
|
||||
a.put("color", "RED");
|
||||
try {
|
||||
a.getEnum("color", Color.class);
|
||||
a.getEnum("color");
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String expected =
|
||||
"Attribute 'color' is of type [String], but [Color] was expected";
|
||||
"Attribute 'color' is of type [String], but [Enum] was expected";
|
||||
assertThat(ex.getMessage().substring(0, expected.length()), equalTo(expected));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,34 +109,34 @@ public class AnnotationMetadataTests {
|
||||
{ // perform tests with classValuesAsString = false (the default)
|
||||
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
|
||||
assertThat(specialAttrs.size(), is(6));
|
||||
assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz", Object.class)));
|
||||
assertThat(specialAttrs.getEnum("state", Thread.State.class), is(Thread.State.NEW));
|
||||
assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz")));
|
||||
assertTrue(specialAttrs.getEnum("state").equals(Thread.State.NEW));
|
||||
|
||||
AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno");
|
||||
assertThat("na", is(nestedAnno.getString("value")));
|
||||
assertThat(nestedAnno.getEnum("anEnum", SomeEnum.class), is(SomeEnum.LABEL1));
|
||||
assertTrue(nestedAnno.getEnum("anEnum").equals(SomeEnum.LABEL1));
|
||||
assertArrayEquals(new Class[]{String.class}, (Class[])nestedAnno.get("classArray"));
|
||||
|
||||
AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray");
|
||||
assertThat(nestedAnnoArray.length, is(2));
|
||||
assertThat(nestedAnnoArray[0].getString("value"), is("default"));
|
||||
assertThat(nestedAnnoArray[0].getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
|
||||
assertTrue(nestedAnnoArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
|
||||
assertArrayEquals(new Class[]{Void.class}, (Class[])nestedAnnoArray[0].get("classArray"));
|
||||
assertThat(nestedAnnoArray[1].getString("value"), is("na1"));
|
||||
assertThat(nestedAnnoArray[1].getEnum("anEnum", SomeEnum.class), is(SomeEnum.LABEL2));
|
||||
assertTrue(nestedAnnoArray[1].getEnum("anEnum").equals(SomeEnum.LABEL2));
|
||||
assertArrayEquals(new Class[]{Number.class}, (Class[])nestedAnnoArray[1].get("classArray"));
|
||||
assertArrayEquals(new Class[]{Number.class}, nestedAnnoArray[1].getClassArray("classArray"));
|
||||
|
||||
AnnotationAttributes optional = specialAttrs.getAnnotation("optional");
|
||||
assertThat(optional.getString("value"), is("optional"));
|
||||
assertThat(optional.getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
|
||||
assertTrue(optional.getEnum("anEnum").equals(SomeEnum.DEFAULT));
|
||||
assertArrayEquals(new Class[]{Void.class}, (Class[])optional.get("classArray"));
|
||||
assertArrayEquals(new Class[]{Void.class}, optional.getClassArray("classArray"));
|
||||
|
||||
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
|
||||
assertThat(optionalArray.length, is(1));
|
||||
assertThat(optionalArray[0].getString("value"), is("optional"));
|
||||
assertThat(optionalArray[0].getEnum("anEnum", SomeEnum.class), is(SomeEnum.DEFAULT));
|
||||
assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
|
||||
assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray"));
|
||||
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user