Class AnnotationBuilder<A extends Annotation>

  • Type Parameters:
    A - annotation type to build

    @Immutable
    public final class AnnotationBuilder<A extends Annotation>
    extends Object
    Builder pattern for creating annotation type instances.

    Sometimes library methods require passing annotation instance, assuming that client will obtain it from reflection on some member or type. This is often poor design, but sometimes some API actually is defined by annotations rather than usual program structure.

    In these cases, trying to call those methods, client has two straightforward options:

    • Create token members or types with required annotation them and extract this annotation before call.
    • Actually implement annotation interface, either by anonymous class place of call, or as a named somewhere near.

    Both of those solutions are problematic. Beside introduction of unnecessary declaration, they also:

    • If different annotation member values are needed, multiple synthetic annotation targets needs to be declared.
    • Annotation interfaces as a rule should not be implemented, and static checkers will mark. Implemented interface will often have repeated constructs, and should have toString, hashCode and equals implemented, but often it doesn't.

    This class helps create instances of annotation type that are indistinguishable from ones extracted by reflection. These have same toString and hashCode and equals that works with native ones.

    If annotation has no members (is effectively a marker), it can be completely constructed by marker(java.lang.Class<A>) call.

    Otherwise annotation instance should be build starting with of(java.lang.Class<A>), with zero or more calls to with(org.perfectable.introspection.AnnotationBuilder.MemberExtractor<A, X>, X) and then build(). Every member of the annotation (i.e. annotation interface method) that doesn't have default value, needs to have value set, and this requirement is checked in the build() method.

    Instances of this class are immutable, can be shared between threads and stored in global variables. Products of this builder are also immutable, as are all annotation instances.

    Example of marker annotation creation:

         Immutable immutableInstance = AnnotationBuilder.marker(Immutable.class);
     

    Example of more complex annotation creation:

         AnnotationBuilder<Tag> tagBuilder = AnnotationBuilder.of(Tag.class);
         Tag tag1 = tagBuilder.with(Tag::value, "one").build();
         Tag tag2 = tagBuilder.with(Tag::value, "two").build();
         Tags tags = AnnotationBuilder.of(Tags.class).with(Tags::value, new Tag[] { tag1, tag2 }).build();
     
    • Method Detail

      • marker

        public static <A extends Annotation> A marker​(Class<A> annotationType)
        Creates instance of annotation with no members.

        If annotation have members, but all of them are default, this method will still

        This method is pure - with the same arguments will produce equivalent results.

        Type Parameters:
        A - type of annotation to build
        Parameters:
        annotationType - representation of type of annotation to build
        Returns:
        Only annotation instance that can be created for this annotation.
        Throws:
        IllegalArgumentException - when provided class does not represent an annotation interface
        IllegalArgumentException - when provided annotation type has any members
      • of

        public static <A extends AnnotationAnnotationBuilder<A> of​(Class<A> annotationType)
        Creates unconfigured builder for specified annotation type.
        Type Parameters:
        A - type of annotation to build
        Parameters:
        annotationType - representation of type of annotation to build
        Returns:
        Fresh, unconfigured builder
        Throws:
        IllegalArgumentException - when provided class does not represent an annotation interface
      • with

        public <X> AnnotationBuilder<A> with​(AnnotationBuilder.MemberExtractor<A,​X> member,
                                             X value)
        Configures member of created annotation instances to have specified value.

        Member is selected by client by providing method reference.

        Builder will reject setting value for a member that has already value configured.

        Type Parameters:
        X - type of member
        Parameters:
        member - reference to method which represents a configured member
        value - value for specified member in annotation instance
        Returns:
        new builder, with additional member configured
        Throws:
        IllegalArgumentException - when member is not a method reference to member of this builders annotation type or when value is not actually instance of member type
      • build

        public A build()
        Creates annotation instance.

        Performs member validation: each non-default member must have configured value.

        Returns:
        annotation instance with configured members