Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [0.1.4] - 2025-08-07

### Added
- N/A

### Changed
- N/A
- Modified `ArrayTypeName.of()` to `ArrayTypeName.get()` for consistency with other TypeName methods

### Fixed
- N/A
- Fixed interface methods with no modifiers incorrectly generating function bodies - they now correctly generate as abstract method declarations (e.g., `void word();` instead of `void word() { }`)

## [0.1.3] - 2025-08-04
- Moved all common ClassNames to be under ClassName instead of TypeName
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,34 @@ public interface Drawable {
}
```

**Interface Method Modifiers:**

PyJavaPoet correctly handles interface methods with and without explicit modifiers:

**Python Code:**
```python
# Interface method with explicit public modifier
interface_with_modifiers = TypeSpec.interface_builder("TestInterface") \
.add_modifiers(Modifier.PUBLIC) \
.add_method(MethodSpec.method_builder("word")
.add_modifiers(Modifier.PUBLIC)
.returns("void")
.build()) \
.build()

java_file = JavaFile.builder("com.example", interface_with_modifiers).build()
print(java_file)
```

**Generated Java Code:**
```java
package com.example;

public interface TestInterface {
public void word();
}
```

### 3. Creating Enums with Custom Methods

**Python Code:**
Expand Down
22 changes: 18 additions & 4 deletions pyjavapoet/method_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def __init__(
code: Optional["CodeBlock"],
default_value: Optional["CodeBlock"],
kind: "MethodSpec.Kind",
in_interface: bool,
):
self.name = name
self.modifiers = modifiers
Expand All @@ -71,7 +72,7 @@ def __init__(
self.code = code
self.default_value = default_value
self.kind = kind

self.in_interface = in_interface
# Validate that constructors don't have a return type
if self.kind == MethodSpec.Kind.CONSTRUCTOR and self.return_type is not None:
raise ValueError("Constructors cannot have a return type")
Expand Down Expand Up @@ -131,12 +132,16 @@ def emit(self, code_writer: "CodeWriter") -> None:
exception.emit(code_writer)

# Emit body or semicolon
if Modifier.ABSTRACT in self.modifiers or (Modifier.NATIVE in self.modifiers and not self.default_value):
code_writer.emit(";\n")
elif self.default_value is not None:
if self.default_value:
code_writer.emit(" default ")
self.default_value.emit(code_writer)
code_writer.emit(";\n")
elif (
Modifier.ABSTRACT in self.modifiers
or Modifier.NATIVE in self.modifiers
or (self.in_interface and Modifier.DEFAULT not in self.modifiers)
):
code_writer.emit(";\n")
else:
code_writer.emit(" {\n")
code_writer.indent()
Expand All @@ -160,6 +165,7 @@ def to_builder(self) -> "MethodSpec.Builder":
deep_copy(self.annotations),
self.code.to_builder() if self.code else CodeBlock.builder(),
deep_copy(self.default_value),
self.in_interface,
)

@staticmethod
Expand Down Expand Up @@ -191,6 +197,7 @@ class Builder(Code.Builder["MethodSpec"]):
__annotations: list["AnnotationSpec"]
__code_builder: "CodeBlock.Builder"
__default_value: Optional["CodeBlock"]
__in_interface: bool

def __init__(
self,
Expand All @@ -205,6 +212,7 @@ def __init__(
annotations: list["AnnotationSpec"] | None = None,
code_builder: Optional["CodeBlock.Builder"] = None,
default_value: Optional["CodeBlock"] = None,
in_interface: bool = False,
):
self.__name = name
self.__kind = kind
Expand All @@ -217,6 +225,7 @@ def __init__(
self.__annotations = annotations or []
self.__code_builder = code_builder or CodeBlock.builder()
self.__default_value = default_value
self.__in_interface = in_interface

def add_modifiers(self, *modifiers: Modifier) -> "MethodSpec.Builder":
self.__modifiers.update(modifiers)
Expand Down Expand Up @@ -356,6 +365,10 @@ def default_value(self, format_string: str, *args) -> "MethodSpec.Builder":
self.__default_value = CodeBlock.of(format_string, *args)
return self

def in_interface(self) -> "MethodSpec.Builder":
self.__in_interface = True
return self

def set_name(self, name: str) -> "MethodSpec.Builder":
self.__name = name
return self
Expand Down Expand Up @@ -384,4 +397,5 @@ def build(self) -> "MethodSpec":
self.__code_builder.build() if self.__code_builder else None,
deep_copy(self.__default_value),
deep_copy(self.__kind),
self.__in_interface,
)
2 changes: 1 addition & 1 deletion pyjavapoet/type_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def copy(self) -> "ArrayTypeName":
return ArrayTypeName(deep_copy(self.component_type), deep_copy(self.annotations))

@staticmethod
def of(component_type: Union["TypeName", str, type]) -> "ArrayTypeName":
def get(component_type: Union["TypeName", str, type]) -> "ArrayTypeName":
return ArrayTypeName(TypeName.get(component_type))


Expand Down
3 changes: 3 additions & 0 deletions pyjavapoet/type_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,9 @@ def add_method(self, method_spec: MethodSpec) -> "TypeSpec.Builder":
if method_spec.kind in (MethodSpec.Kind.CONSTRUCTOR, MethodSpec.Kind.COMPACT_CONSTRUCTOR):
method_spec = method_spec.to_builder().set_name(self.__name).build()

if self.__kind == TypeSpec.Kind.INTERFACE:
method_spec = method_spec.to_builder().in_interface().build()

self.__methods.append(method_spec)
return self

Expand Down
15 changes: 15 additions & 0 deletions tests/test_method_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ def test_abstract_method(self):
# Abstract methods should not have a body
self.assertNotIn("{", result)

def test_method_in_interface(self):
"""Test method in interface."""
method = (
MethodSpec.method_builder("process")
.returns("void")
.add_parameter(ClassName.get("java.lang", "Object"), "data")
.in_interface()
.build()
)

result = str(method)
self.assertIn("void process", result)
# Interface methods should not have a body
self.assertNotIn("{", result)

def test_method_with_exceptions(self):
"""Test method with exceptions."""
method = (
Expand Down
10 changes: 5 additions & 5 deletions tests/test_type_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ def test_equals_and_hash_code_class_name(self):

def test_equals_and_hash_code_array_type_name(self):
"""Test equals and hash code for array types."""
a = ArrayTypeName.of(ClassName.get("java.lang", "String"))
b = ArrayTypeName.of(ClassName.get("java.lang", "String"))
c = ArrayTypeName.of(ClassName.get("java.lang", "Object"))
a = ArrayTypeName.get(ClassName.get("java.lang", "String"))
b = ArrayTypeName.get(ClassName.get("java.lang", "String"))
c = ArrayTypeName.get(ClassName.get("java.lang", "Object"))

self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
Expand Down Expand Up @@ -167,11 +167,11 @@ def test_can_box_annotated_primitive(self):

def test_array_type_creation(self):
"""Test array type creation."""
string_array = ArrayTypeName.of(ClassName.get("java.lang", "String"))
string_array = ArrayTypeName.get(ClassName.get("java.lang", "String"))
self.assertEqual(str(string_array), "String[]")

# Multi-dimensional array
int_2d_array = ArrayTypeName.of(ArrayTypeName.of(TypeName.get("int")))
int_2d_array = ArrayTypeName.get(ArrayTypeName.get(TypeName.get("int")))
self.assertEqual(str(int_2d_array), "int[][]")

def test_type_variable_with_bounds(self):
Expand Down
11 changes: 9 additions & 2 deletions tests/test_type_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,19 @@ def test_interface_creation(self):
.returns("void")
.build()
)
.add_method(MethodSpec.method_builder("random").returns("void").build())
.build()
)

result = str(drawable)
self.assertIn("public interface Drawable", result)
self.assertIn("public abstract void draw()", result)
expected = """\
public interface Drawable {
public abstract void draw();

void random();
}\
"""
self.assertEqual(result, expected)

def test_interface_with_default_method(self):
"""Test interface with default method."""
Expand Down
Loading