Skip to content

Commit e698af9

Browse files
authored
A default method on blueprint is now also default on prototype and calls the method on blueprint (helidon-io#11061)
1 parent d8a5b9d commit e698af9

2 files changed

Lines changed: 92 additions & 3 deletions

File tree

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerBasic.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import io.helidon.common.types.Annotation;
3636
import io.helidon.common.types.Annotations;
3737
import io.helidon.common.types.ElementKind;
38+
import io.helidon.common.types.Modifier;
3839
import io.helidon.common.types.TypeInfo;
3940
import io.helidon.common.types.TypeName;
4041
import io.helidon.common.types.TypeNames;
@@ -905,6 +906,25 @@ private Optional<GeneratedMethod> preparePrototypeGetter(Javadoc javadoc) {
905906
}
906907
}
907908

909+
boolean callSuper = false;
910+
TypeName declaringType;
911+
if (override) {
912+
TypeName tmpDeclaringType = null;
913+
if (option().interfaceMethod().isPresent()) {
914+
var interfaceMethod = option().interfaceMethod().get();
915+
if (interfaceMethod.elementModifiers().contains(Modifier.DEFAULT)) {
916+
// in case we do not have a declaring type, we cannot call the super method
917+
// as default methods on interfaces may inherit from more than one super interface, and the
918+
// invocation is SuperType.super.methodName()
919+
tmpDeclaringType = interfaceMethod.enclosingType().orElse(null);
920+
callSuper = tmpDeclaringType != null;
921+
}
922+
}
923+
declaringType = tmpDeclaringType;
924+
} else {
925+
declaringType = null;
926+
}
927+
908928
var method = TypedElementInfo.builder()
909929
.kind(ElementKind.METHOD)
910930
.accessModifier(AccessModifier.PUBLIC)
@@ -917,11 +937,23 @@ private Optional<GeneratedMethod> preparePrototypeGetter(Javadoc javadoc) {
917937
method.addAnnotation(Annotations.OVERRIDE);
918938
}
919939

940+
Consumer<ContentBuilder<?>> contentConsumer;
941+
if (callSuper) {
942+
method.addElementModifier(Modifier.DEFAULT);
943+
contentConsumer = it -> it.addContent("return ")
944+
.addContent(declaringType)
945+
.addContent(".super.")
946+
.addContent(option().getterName())
947+
.addContentLine("();");
948+
} else {
949+
// interface method
950+
contentConsumer = it -> {};
951+
}
952+
920953
return Optional.of(GeneratedMethod.builder()
921954
.method(method.build())
922955
.javadoc(javadoc)
923-
.contentBuilder(it -> {
924-
}) // always an interface method
956+
.contentBuilder(contentConsumer)
925957
.build());
926958
}
927959

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.builder.test;
18+
19+
import java.util.Optional;
20+
21+
import io.helidon.builder.test.testsubjects.A;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
import static org.hamcrest.CoreMatchers.is;
26+
import static org.hamcrest.MatcherAssert.assertThat;
27+
28+
/*
29+
Make sure the A from ABlueprint can be extended without implementing the default method
30+
*/
31+
class BackwardCompatibilityTest {
32+
@Test
33+
void testDefaultMethodValue() {
34+
A a = A.builder()
35+
.a("hello")
36+
.build();
37+
38+
assertThat(a.a(), is("hello"));
39+
assertThat(a.aNewProperty(), is(Optional.empty()));
40+
}
41+
42+
@Test
43+
void testFoo() {
44+
// make sure the class compiles and the default method on `A` is truly default
45+
Foo foo = new Foo();
46+
47+
assertThat(foo.a(), is(""));
48+
}
49+
50+
// this class must compile, as a() should be the only non-default method
51+
static class Foo implements A {
52+
@Override
53+
public String a() {
54+
return "";
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)