Factory methods & the language model
Dan Smith
daniel.smith at oracle.com
Thu Sep 9 14:23:41 UTC 2021
JEP 401 includes special JVM factory methods, spelled <new> (or, alternatively, <init> with a non-void return), which are needed as a standardized way to encode the Java language's primitive class constructors.
We have a lot of flexibility in how much we restrict use of these methods. Too many restrictions seem arbitrary and incoherent from the JVM's point of view; but too few restrictions risk untested corner cases, unfortunate compatibility obligations, and difficulties mapping back to the Java language model.
Expanding on that last one: for tools that operate with a Java language model, there are essentially three strategies for dealing with factory methods outside of the core primitive class construction use case:
1) Have the JVM reject them
2) Ignore them
3) Expand the model to include them
Taking javac as an example, here's what that looks like:
1) If factory methods outside of primitive classes are illegal, javac can treat classes with such methods as malformed and report an error.
2) Or if javac sees a factory method in a non-primitive class, it can just leave it out when it maps the class file to a language-level class. (There's precedent for this in, e.g., the treatment of fields with the same name and different descriptors.)
3) Or we can allow javac to view factory methods in any class as constructors. A few complications:
- Constructors of non-final classes have both 'new Foo()' and 'super()' entry points; factories only support the first. So we either need to validate that a matching pair of <new> and <init> exist, or expand the language to model factories independently from constructors.
- The language expects instance creation expressions to create fresh instances. We need to either validate this behavior (does the factory look like "new/dup/<init>"?) or relax the language semantics (perhaps this is in the grey area of mixed binaries?)
- Factories can appear in abstract classes and interfaces. Again, are we willing to change the language model to support these use cases? Perhaps to even allow their declaration?
- If a factory method has a mismatched return type (declared in Foo, but returns a Bar), are we willing to support a type system where the type of a factory invocation is not the type of the class to which the factory belongs?
There are probably limits to what we're willing to do with (3), which pushes at least some cases into the (1) or (2) buckets.
So, my question: what should we expect from (3), now and in the foreseeable future? And for the cases that fall outside of it, should we fall back to (1), (2), or a mixture of both?
More information about the valhalla-spec-observers
mailing list