diff --git a/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt b/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt index 965c2143..8f22e461 100644 --- a/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt +++ b/pkl-codegen-java/src/main/kotlin/org/pkl/codegen/java/JavaCodeGenerator.kt @@ -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) diff --git a/pkl-codegen-java/src/test/kotlin/org/pkl/codegen/java/JavaCodeGeneratorTest.kt b/pkl-codegen-java/src/test/kotlin/org/pkl/codegen/java/JavaCodeGeneratorTest.kt index 811112d9..a4eb1239 100644 --- a/pkl-codegen-java/src/test/kotlin/org/pkl/codegen/java/JavaCodeGeneratorTest.kt +++ b/pkl-codegen-java/src/test/kotlin/org/pkl/codegen/java/JavaCodeGeneratorTest.kt @@ -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