All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ryandens.delegation.AutoDelegate Maven / Gradle / Ivy

package com.ryandens.delegation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Decorates a class with metadata to describe an abstract parent class that automatically delegates
 * to an inner composed instance of an interface. This annotation processor is inspired by the
 * Kotlin language feature delegation.
 *
 * 

The goal of this is to encourage the use of composition over inheritance as described by * Effective Java Item 18 "Favor composition over inheritance". In the section of the book, Bloch * describes an {@code InstrumentedSet} that counts the number of items added to it. In order to * accomplish this, Bloch creates an abstract implementation of {@link java.util.Set} called {@code * ForwardingSet} that simply composes a {@link java.util.Set} instance and forwards all calls to * it. This allows Bloch to write the {@code InstrumentedSet} in a less verbose manner, by extending * {@code ForwardingSet} and overriding the "add" related methods for instrumentation purposes. This * is a great solution in the context of Java, but Kotlin lowers the cognitive barrier of using * composition by making it less verbose to do so. In Kotlin, the need for a {@code ForwardingSet} * is obviated by the "delegation" language feature linked above. The {@code InstrumentedSet} can be * written concisely without relying on writing a {@code ForwardingSet} like: * *

{@code
 * class InstrumentedSet(val inner: MutableSet) : MutableSet by inner {
 *     var count: Int = 0
 *
 *     override fun add(element: E): Boolean {
 *         count++
 *         return inner.add(element)
 *     }
 *
 *     override fun addAll(elements: Collection) : Boolean {
 *         count += elements.size
 *         return inner.addAll(elements)
 *     }
 * }
 * }
* *

This annotation strives to enable developers in the same fashion by generating abstract {@code * Forwarding*} classes that delegate to the inner composed instance. An equivalent {@code * InstrumentedSet} implementation written with {@code AutoDelegate} is * *

{@code
 * @AutoDelegate(Set.class)
 * public final class InstrumentedSet extends AutoDelegate_InstrumentedSet implements Set {
 *   private int addCount;
 *
 *   public InstrumentedSet(final Set inner) {
 *     super(inner);
 *     this.addCount = 0;
 *   }
 *
 *   @Override
 *   public boolean add(final E t) {
 *     addCount++;
 *     return super.add(t);
 *   }
 *
 *   @Override
 *   public boolean addAll(final Collection c) {
 *     addCount += c.size();
 *     return super.addAll(c);
 *   }
 *
 *   public int addCount() {
 *     return addCount;
 *   }
 * }
 * }
* *

While this is not as concise as the Kotlin implementation, it generates a class called {@code * AutoDelegate_InstrumentedSet} in the same package as the declaring class. The declared class can * then extend the generated class and call {code super} APIs where appropriate, only overriding * methods that are relevant to the implementation * *

In addition, this annotation supports targeting multiple interfaces for delegation rather than * just one. This is useful when separating the responsibilities of a monolithic class into multiple * separate classes with one responsibility, but still tying them all together via a single concrete * instance. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface AutoDelegate { /** * @return an interface that the generated class should delegate to via an inner composed * instance. Note,the class annotated by this {@link AutoDelegate} element must be assignable * from the class provided in this annotation. * @throws IllegalArgumentException if this array is not {@link Void#getClass()} and {@link #to()} * is not empty, meaning only one of these values should be specified in a usage of this * annotation */ Class value() default void.class; /** * @return the interfaces that the generated class should delegate to via inner composes * instances. Note, the class annotated by this {@link AutoDelegate} annotation must be * assignable from each of the classes provided with this annotation. * @throws IllegalArgumentException if this array is not empty and {@link #value()} is not {@link * Void#getClass()}, meaning only one of these values should be specified in a usage of this * * annotation */ Class[] to() default {}; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy