Interface Instantiator


@API(status=MAINTAINED) public interface Instantiator
Service capable of generating runtime implementations of interfaces.

Callers may use hasProduced(Object) to check whether a configuration object came from this instantiator.

Implementing

Implementing this interface correctly requires a number of considerations. It is recommended to check the library source code as a reference implementation.

Generated code must handle bridge and synthetic methods of its own accord. Users of this interface, like users of MethodMirror, are expected to be externally ignorant of bridge and synthetic methods, insofar as caller code will look identical regardless of the presence of bridge and synthetic methods.

Equality

An Instantiator is supposed to implement equality among instances produced by it. Instances produced by other Instantiator implementations should never be equal to instances produced by this one. Each generating method describes the equality considerations which must be upheld.

Notably, all methods must consider the leading iface parameter in calculating equality. The iface identifies the interface being implemented, and if this interface differs between instances produced by this instantiator, then those instances cannot be equal. This holds true not only for instances produced by the same generating method, but also for equality between instances from different generating methods.

Because of the reflexive property of equals, generated instances may need to be aware of each others' equality contracts even if they are produced by different generating methods. Additionally, hashCode must be implemented in a manner compatible with the equality contract.

  • Method Summary

    Modifier and Type
    Method
    Description
    <I> @NonNull I
    generate(@NonNull Class<I> iface, @NonNull MethodYield methodYield)
    Generates the target class.
    <I> @NonNull I
    generateEmpty(@NonNull Class<I> iface)
    Generates an "empty" implementation for the given interface type, which lets the caller use its default method implementations.
    <I> @NonNull ReloadShell<I>
    generateShell(@NonNull Class<I> iface)
    Makes a reloadable shell for the given interface type.
    boolean
    hasProduced(@NonNull Object instance)
    Checks whether this instantiator produced the specified object.
  • Method Details

    • hasProduced

      boolean hasProduced(@NonNull Object instance)
      Checks whether this instantiator produced the specified object.

      All instantiators are required to mark the instances they generate using some unique method. This could be as simple as implementing a local interface as a marker.

      Parameters:
      instance - the object
      Returns:
      true if this instantiator produced it, false otherwise
    • generate

      <I> @NonNull I generate(@NonNull Class<I> iface, @NonNull MethodYield methodYield)
      Generates the target class.

      Speaking logically, this function takes a map of methods to return values, and generates an implementation of the requested interface which, when its methods are called, yields the preconfigured values. This function is therefore an important backbone of the whole library, and the generated instance should operate as performantly as feasible.

      Methods and Values

      The implementor can use MethodYield.entries() to traverse the methods of iface, retrieving return values for each. Every MethodYield.Entry.method() identifies a method belonging to the iface argument, or one of its supertypes. These methods are guaranteed to represent the lowest in the class hierarchy, and they exclude overidden parent methods. In other words, parent methods which are overidden won't be provided; using MethodYield.Entry.implementable() will return the overriding interface.

      Equality

      Calling this function with equal iface and methodYield parameters should yield equal instances. That is to say, an instance is equal to another if it has the same behavior (yields the same values, and calls the same default methods).

      Additionally, an instance should be considered equal to an instance produced by generateEmpty if the MethodYield passed here is empty of preset values (see generateEmpty(Class)). For a shell instance produced by generateShell, please see generateShell(Class).

      Parameters:
      iface - the interface to implement
      methodYield - a map of methods to the values they are to yield, for each type in the hierarchy of iface
      Returns:
      the generated implementation
    • generateShell

      <I> @NonNull ReloadShell<I> generateShell(@NonNull Class<I> iface)
      Makes a reloadable shell for the given interface type.

      Equality

      The returned ReloadShell itself should use identity equality (only be equal to itself). However, the shell instance (from ReloadShell.getShell()) should have the following equality behavior.

      First, it requires other instances to have the same iface argument to be considered equal. This holds true not only for shell instances produced by this method, but also instances produced by generate and generateEmpty.

      Secondly, the delegate should be considered. Two shell instances are equal if they have equal delegates, including potentially null delegates (null equals null). Otherwise, the delegate must be nonnull, and the shell instance should evaluate its own equality by checking if the current delegate is equal (by calling its equals method).

      Type Parameters:
      I - the interface type
      Parameters:
      iface - the interface to implement
      Returns:
      a reload shell
    • generateEmpty

      <I> @NonNull I generateEmpty(@NonNull Class<I> iface)
      Generates an "empty" implementation for the given interface type, which lets the caller use its default method implementations.

      The purpose of this function is for the caller to use the default methods of the provided interface, and the caller promises not to use non-default methods. If that promise is broken, behavior is not defined.

      Equality

      Calling this function with the same iface parameter should yield equal instances.

      The instance should also be considered equal to an instance produced by generate(Class, MethodYield), if and only if the interfaces match and the MethodYield provided to that function is empty of preset values (i.e., excluding the special value InvokeDefaultFunction.) That is to say, an "empty" instance being generated by this method can only be equal to an instance generated by the other method, if the other instance implements the same interface and only invokes default method implementations.

      For a shell instance produced by generateShell(Class), please see

      Type Parameters:
      I - the interface type
      Parameters:
      iface - the interface to implement
      Returns:
      the instance