-2

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;
}
Pwnstar
  • 2,333
  • 2
  • 29
  • 52
  • 1
    I think it would be helpful to provide an [MCVE](https://stackoverflow.com/help/mcve) on GitHub instead of just snippets, forcing everyone to create a demo project from scratch in order to reproduce your problem and guessing about the missing pieces. E.g., it is unclear what `EventObject` looks like and which class exactly inherits from which. But that information is crucial for reasoning about your problem. – kriegaex Mar 15 '23 at 13:15
  • 3
    Note: having no constructor at all means you _have_ the default constructor (`CustomEvent() { }`). Tying this together with the implicit `super()` from a subclass, and I don't really see the problem yet myself. If `#getFoo` is `abstract`, then the subclasses can override it and the super class doesn't need to manage it. – Rogue Mar 15 '23 at 13:48
  • 3
    @kriegaex - MCVE : yes ... but putting it on GitHub: No!! The MCVE or [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) should be in the question itself. GitHub repos can permanently vanish in an instant, leaving the question broken. – Stephen C Mar 18 '23 at 14:20
  • What I care about is easy reproducibility. I have better things to do than copying and pasting a dozen classes, Maven POM and Spring config. Moreover, I can never be sure there is not any directory layout glitch. So no, I cannot agree with your opinion. The main gist should be in the code here, but the full MCVE belongs somewhere I can clone and run it from in an instant. – kriegaex Mar 18 '23 at 18:44
  • And the flipside is that **other people** care more about about this question being meaningful for future readers. Consider what the **stated** primary goal of StackOverflow is. (It is not about satisfying the OP's need for help ... your personal convenience when answering the question.) – Stephen C Mar 19 '23 at 04:54
  • Also, see https://meta.stackoverflow.com/questions/253915. And note that it is legally dubious for us to fix such a mistake; see https://meta.stackoverflow.com/questions/275485. It is potentially a copyright violation ... unless the Gist or Repo has an explicit copyright statement that is legally compatible copying to SO. – Stephen C Mar 19 '23 at 05:06
  • Stop lecturing me. I am here to help others. Let me do it my way. I often answer complex questions about AOP (AspectJ, Spring AOP) or test automation with Spock, and in 50%+ of all cases the problem is in the parts of the code or configuration the OP chose not to share, because she thinks it is irrelevant. It is super tedious to send comments back and forth in 10 iterations. SO is a Q/A platform, not a discussion forum, and the time volunteers spend to help others should be respected. But nobody wants to scroll 25 pages worth of sample code and then copy everything into their respective IDEs. – kriegaex Mar 19 '23 at 08:47
  • This sense of entitlement of some SO users is incomprehensible to me. SO is not an SCM. Vast amounts of code do not belong here. I am always careful to answer questions in a way which makes it clear what the problem was and how to solve it, so they are well documented. An MCVE below a certain number of files and lines of code can be posted here, totally fine. I tend to write long answers with full MCVEs where applicable. But more than half of the questions I answer here are utterly unsolvable without an MCVE way too big to fit in here. – kriegaex Mar 19 '23 at 08:51
  • As for "legally dubious to answer": LOL. – kriegaex Mar 19 '23 at 08:56
  • @kriegaex (we're way off-topic on this question now, but...) I think the big thing that irked was the "instead of": an MCVE on github isn't inherently bad imo, just the question becoming obsolete because of non-reproducibility. If the MCVE is too unwieldly to put into the question it's very likely not minimal, but there's not much reason aside from effort they couldn't do both the github and in-question references. I've honestly found that the best MCVEs don't arrive until after the question is answered and the problem demonstrated. – Rogue Mar 19 '23 at 18:21
  • Read again. I wrote "instead of **just** (snippets)". I didn't mean him to remove info from the question, but to bolster it up some more, supported by the GitHub MCVE. I solved literally dozens of puzzles here only after having had access to one of those, for the benefit of everyone involved. It is called WWW für a reason: being a hyperlinked system. I really couldn't care less about people feeling entitled of having all possible information here on SO. this platform is still useful enough with some info not quoted here but referenced to. – kriegaex Mar 19 '23 at 21:06
  • BTW, I do take pride in having a pretty good code parser in my head, but there are limits to what lengths I am willing to go to in order to help others. A source code repository is simply the most effective way of getting new code into my IDE and bringing an MCVE to life there. Maybe you have enough spare time to copy and paste vast amounts of code. I don't. – kriegaex Mar 19 '23 at 21:10
  • *"As for "legally dubious to answer": LOL."* - You misinterpreted what I said. It is legally dubious to correct the mistake (or bad practice) of the OP linking to a gist by copying their gist into the question. That may be a copyright violation. We are advised NOT to do it. If they copy their code into the question that is fine ... because by doing so they are implicitly granting permission to copy ... as per the T&Cs. – Stephen C Mar 20 '23 at 11:33
  • However this is all moot because the OP hasn't provide an MCVE / minrep in any form. – Stephen C Mar 20 '23 at 11:38

2 Answers2

2

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]
Dave Ankin
  • 1,060
  • 2
  • 9
  • 20
  • I marked this as answer, because right now it seems there is no solution to this with org.immutables – Pwnstar Mar 22 '23 at 10:24
-1

For an immutable class, I would recommend to provide all fields in the (protected) constructor. Keep in mind, the inheriting class must see the parent's constructor in scope.

Since the class shall be immutable, and you want to be sure, it's always safer to have all the fields final (and immutable as well).

When considering the builder-pattern, provide a static sub-class Builder, provide with it the reasonable defaults and builder-methods.

From Java 14+, there is the alternative option to use records, see https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Record.html , which also is feasible to be combined with a builder.

MJG
  • 355
  • 1
  • 9