diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java index 1ed3add66313..90a024b1c899 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -476,6 +476,9 @@ public abstract static class Builder> // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. private boolean isClean; + // Indicates that the default instance can be used + private boolean isDefault = true; + /** * This field holds either an {@link UnknownFieldSet} or {@link UnknownFieldSet.Builder}. * @@ -527,6 +530,15 @@ protected boolean isClean() { return isClean; } + /** + * Gets whether the default instance can be used + * + * @return whether the default instance can be used + */ + protected boolean isDefault() { + return isDefault; + } + @Override public BuilderT clone() { BuilderT builder = (BuilderT) getDefaultInstanceForType().newBuilderForType(); @@ -834,6 +846,7 @@ protected final void onChanged() { // Don't keep dispatching invalidations until build is called again. isClean = false; } + isDefault = false; } /** diff --git a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java index b48c304c7d11..2c839aa65968 100644 --- a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java +++ b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java @@ -10,6 +10,7 @@ final class NewInstanceSchemaFull implements NewInstanceSchema { @Override public Object newInstance(Object defaultInstance) { - return ((Message) defaultInstance).toBuilder().buildPartial(); + return ((GeneratedMessage) defaultInstance) + .newInstance(GeneratedMessage.UnusedPrivateParameter.INSTANCE); } } diff --git a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java index 39e0b5dba40b..f976c7079c67 100644 --- a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java +++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java @@ -156,6 +156,20 @@ public void testMerge() { assertThat(vehicle1.getWheelList()).isSameInstanceAs(vehicle3.getWheelList()); } + @Test + public void testUsesDefaultInstance() { + Vehicle vehicle1 = Vehicle.newBuilder().build(); + // Should use the default instance -- no allocation + assertThat(vehicle1).isSameInstanceAs(Vehicle.getDefaultInstance()); + + Vehicle vehicle2 = + Vehicle.newBuilder() + .addWheel(Wheel.newBuilder().build()) + .build(); + // Should use the default instance -- no allocation + assertThat(vehicle2.getWheel(0)).isSameInstanceAs(Wheel.getDefaultInstance()); + } + @Test public void testGettingBuilderMarksFieldAsHaving() { Vehicle.Builder vehicleBuilder = Vehicle.newBuilder(); diff --git a/src/google/protobuf/compiler/java/immutable/message.cc b/src/google/protobuf/compiler/java/immutable/message.cc index 0d75c69efb74..11b726a50e38 100644 --- a/src/google/protobuf/compiler/java/immutable/message.cc +++ b/src/google/protobuf/compiler/java/immutable/message.cc @@ -363,6 +363,15 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { "}\n" "\n"); + printer->Print(variables, + "@java.lang.Override\n" + "@SuppressWarnings({\"unused\"})\n" + "protected java.lang.Object newInstance(\n" + " UnusedPrivateParameter unused) {\n" + " return new $classname$();\n" + "}\n" + "\n"); + GenerateDescriptorMethods(printer); // Nested types diff --git a/src/google/protobuf/compiler/java/immutable/message_builder.cc b/src/google/protobuf/compiler/java/immutable/message_builder.cc index 3bc0900c16f8..9d7a2dc6627a 100644 --- a/src/google/protobuf/compiler/java/immutable/message_builder.cc +++ b/src/google/protobuf/compiler/java/immutable/message_builder.cc @@ -475,6 +475,7 @@ void MessageBuilderGenerator::GenerateBuildPartial(io::Printer* printer) { printer->Print( "@java.lang.Override\n" "public $classname$ buildPartial() {\n" + " if (isDefault()) { onBuilt(); return $classname$.getDefaultInstance(); }\n" " $classname$ result = new $classname$(this);\n", "classname", name_resolver_->GetImmutableClassName(descriptor_));