javaspring-bootimmutables-library

org immutables - no default constructor on abstract class


Using https://immutables.github.io/ I wonder if it is possible to have something like custom immutable abstract class inheriting from a class without a default constructor. In this example a sub of Spring's ApplicationEvent (and also take advantage of builder functionality):

@Value.Immutable
@Value.Style(
    privateNoargConstructor = true,
    get = {"is*", "get*"},
    init = "set*",
    passAnnotations = Builder.class)
public abstract class CustomEvent extends ApplicationEvent {
    //... I need constructor here!

    abstract String getFoo();
}

How would you accomplish this if you have no default constructor on the abstract class?

public abstract class ApplicationEvent extends EventObject {
    ...
    public ApplicationEvent(Object source) {
        super(source);
        ...
    }
}

EDIT:

If I create a matching constructor like:

private CustomEvent(Object source) {
    super(source);
}

I will get a "generated" ImmutableCustomEvent constructor like this:

private ImmutableCustomEvent() {
    this.foo = null;
}

Which makes sense, as it tries to generate a class with all the "properties" necessary, but does not consider the "only available" constructor

EDIT2:

What I expect as a generated constructor

private ImmutableCustomEvent() {
    super(null)
    this.foo = null;
}

or at least

private ImmutableCustomEvent(Object source) {
    super(source)
    this.foo = null;
}

Solution

  • So it appears that immutables does not currently generate constructors with super calls, like you need to subclass ApplicationEvent in spring, as you need "a constructor matching super".

    As you can see here, the constructor template goes from the javadoc for the constructor straight into enumerating arguments. the Invokable class being referenced here is from guava (30.0 on the latest immutables) represents a method signature.

    I believe that this line is some kind of hook into the immutables templating system (that would need to be expanded to support generating super constructor calls). As you can see it appears right after the opening curly brace of the constructor (and an if statement that decides to generate that constructor.

    The templating system is complicated, and it might be discontinued in the future. But it seems they are receptive to feedback about redesigning extensibility for new use cases.

    [template generateConstructor Type type Boolean withoutOptional Invokable constructorAcceptTypeInvokable]
      [if type.factoryOf.new]
      /**
       * Construct a new immutable {@code [type.name]} instance.
      [for v in type.constructorArguments]
       * @param [v.name] The value for the {@code [v.name]} attribute[if v.nullable], can be {@code null}[/if]
      [/for]
       */
      [eachLine type.constructorAnnotations]
    ...
      private [type.typeImmutable.simple]([output.linesShortable][for v in type.constructorArguments][if not for.first],[/if]
          [v.atNullability][constructorAcceptTypeInvokable v] [v.name][/for][/output.linesShortable]) {
      [/if]
    [if type.constructorOmited or (type.hasEncodingValueOrVirtualFields or (type.generateSafeDerived and type.hasDerivedAttributes))]
    
      ### not sure what this does, but it is the first thing that happens
      ### when a constructor is generated
      [let shim][disambiguateField type 'initShim'][/let]
    
      ### probably just need to add something here that considers what supers exist
    
      ### then goes right into args
      [for v in type.constructorArguments, n = v.name]
    ...
        this.[n] = [valueFrom type v n withoutOptional];
    ...
    [/if]
        [generateAfterConstruction type false]
      }
    [/template]