Java 8 introduces both Lambda Expressions and Type Annotations.
With type annotations, it is possible to define Java annotations like the following:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface MyTypeAnnotation {
public String value();
}
One can then use this annotation on any type reference like e.g.:
Consumer<String> consumer = new @MyTypeAnnotation("Hello ") Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Here is a complete example, that uses this annotation to print "Hello World":
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Java8Example {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface MyTypeAnnotation {
public String value();
}
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, new @MyTypeAnnotation("Hello ") Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
});
}
public static void testTypeAnnotation(List<String> list, Consumer<String> consumer){
MyTypeAnnotation annotation = null;
for (AnnotatedType t : consumer.getClass().getAnnotatedInterfaces()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break;
}
}
for (String str : list) {
if (annotation != null) {
System.out.print(annotation.value());
}
consumer.accept(str);
}
}
}
The output will be:
Hello World!
Hello Type Annotations!
In Java 8 one can also replace the anonymous class in this example with a lambda expression:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, p -> System.out.println(p));
}
But since the compiler infers the Consumer type argument for the lambda expression, one is no longer able to annotate the created Consumer instance:
testTypeAnnotation(list, @MyTypeAnnotation("Hello ") (p -> System.out.println(p))); // Illegal!
One could cast the lambda expression into a Consumer and then annotate the type reference of the cast expression:
testTypeAnnotation(list,(@MyTypeAnnotation("Hello ") Consumer<String>) (p -> System.out.println(p))); // Legal!
But this will not produce the desired result, because the created Consumer class will not be annotated with the annotation of the cast expression. Output:
World!
Type Annotations!
Two questions:
Is there any way to annotate a lambda expression similar to annotating a corresponding anonymous class, so one gets the expected "Hello World" output in the example above?
In the example, where I did cast the lambda expression and annotated the casted type: Is there any way to receive this annotation instance at runtime, or is such an annotation always implicitly restricted to RetentionPolicy.SOURCE?
The examples have been tested with javac and the Eclipse compiler.
Update
I tried the suggestion from @assylias, to annotate the parameter instead, which produced an interesting result. Here is the updated test method:
public static void testTypeAnnotation(List<String> list, Consumer<String> consumer){
MyTypeAnnotation annotation = null;
for (AnnotatedType t : consumer.getClass().getAnnotatedInterfaces()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break;
}
}
if (annotation == null) {
// search for annotated parameter instead
loop: for (Method method : consumer.getClass().getMethods()) {
for (AnnotatedType t : method.getAnnotatedParameterTypes()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break loop;
}
}
}
}
for (String str : list) {
if (annotation != null) {
System.out.print(annotation.value());
}
consumer.accept(str);
}
}
Now, one can also produce the "Hello World" result, when annotating the parameter of an anonymous class:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, new Consumer<String>() {
@Override
public void accept(@MyTypeAnnotation("Hello ") String str) {
System.out.println(str);
}
});
}
But annotating the parameter does not work for lambda expressions:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, (@MyTypeAnnotation("Hello ") String str) -> System.out.println(str));
}
Interestingly, it is also not possible to receive the name of the parameter (when compiling with javac -parameter), when using a lambda expression. I'm not sure though, if this behavior is intended, if parameter annotations of lambdas have not yet been implemented, or if this should be considered a bug of the compiler.