codegen-java: Make stateless classes instantiable (#734)

Motivation:
Currently, the condition for making a generated Java class instantiable is "class is neither abstract nor stateless".
(An instantiable class receives a public constructor and equals/hashCode/toString/with methods.)
This is inconsistent with Pkl and codegen-kotlin, both of which support instantiation of stateless classes.

Changes:
Widen condition for making a class instantiable to "class is neither abstract nor a stateless final module class".
This is consistent with codegen-kotlin, which generates object declarations (only) for stateless final module classes.

Result:
Stateless classes are instantiable in Pkl, codegen-java, and codegen-kotlin.
This commit is contained in:
translatenix
2024-10-24 09:29:08 -07:00
committed by GitHub
parent 22c9a6c9f4
commit cbbcca0d84
2 changed files with 92 additions and 4 deletions

View File

@@ -541,8 +541,9 @@ class JavaCodeGenerator(
val builder =
TypeSpec.classBuilder(javaPoetClassName.simpleName()).addModifiers(Modifier.PUBLIC)
// stateless classes aren't instantiable by choice, i.e., no public ctor is generated
val isInstantiable = !pClass.isAbstract && allProperties.isNotEmpty()
// stateless final module classes are non-instantiable by choice
val isInstantiable =
!(pClass.isAbstract || (isModuleClass && !pClass.isOpen && allProperties.isEmpty()))
if (codegenOptions.implementSerializable && isInstantiable) {
builder.addSuperinterface(java.io.Serializable::class.java)

View File

@@ -775,6 +775,83 @@ class JavaCodeGeneratorTest {
.isEqualToResourceFile("Inheritance.jva")
}
@Test
fun `stateless classes`() {
val javaCode =
generateJavaCode(
"""
module my.mod
class Foo
abstract class Bar
class Baz extends Bar
"""
.trimIndent()
)
assertThat(javaCode)
.contains(
"""
| public static final class Foo {
| public Foo() {
| }
"""
.trimMargin()
)
.contains(
"""
| public abstract static class Bar {
| protected Bar() {
| }
"""
.trimMargin()
)
.contains(
"""
| public static final class Baz extends Bar {
| public Baz() {
| }
"""
.trimMargin()
)
}
@Test
fun `stateless module classes`() {
var javaCode = generateJavaCode("module my.mod")
assertThat(javaCode)
.contains(
"""
|public final class Mod {
| private Mod() {
| }
"""
.trimMargin()
)
javaCode = generateJavaCode("abstract module my.mod")
assertThat(javaCode)
.contains(
"""
|public abstract class Mod {
| protected Mod() {
| }
"""
.trimMargin()
)
javaCode = generateJavaCode("open module my.mod")
assertThat(javaCode)
.contains(
"""
|public class Mod {
| public Mod() {
| }
"""
.trimMargin()
)
}
@Test
fun `reserved words`() {
val props = javaReservedWords.joinToString("\n") { "`$it`: Int" }
@@ -1765,12 +1842,22 @@ class JavaCodeGeneratorTest {
@Test
fun `non-instantiable classes aren't made serializable`() {
val javaCode =
var javaCode =
generateJavaCode(
"""
module my.mod
abstract class Foo { str: String }
class Bar // non-instantiable because no constructor is generated for stateless class
"""
.trimIndent(),
implementSerializable = true
)
assertThat(javaCode).doesNotContain("Serializable")
javaCode =
generateJavaCode(
"""
module my.mod
"""
.trimIndent(),
implementSerializable = true