Currently, merging from a partial will raise an UnsupportedOperationException if any required field is unset. This was to avoid a performance penalty in non-test code.
However, I think we can use the same trick that worked for the static toBuilder method: cast to a common interface, then call a method (say, mergeInto) that has different implementations in the Value and Partial types. If the cast fails, fall back to the old code path. This avoids a performance hit because hotspot will determine a single type is used in practice and optimize for it.
One risk in doing this is that people might start using partials in production code for this behaviour. We would need to document the preferred alternative (namely, merge from a partially filled Builder).
This still doesn't make partiality "stick" to the builder in the same way it does for toBuilder, though that also doesn't happen if you use mergeFrom(partial.toBuilder()). It may still make testing sufficiently easier that it is worth doing.
Currently, merging from a partial will raise an UnsupportedOperationException if any required field is unset. This was to avoid a performance penalty in non-test code.
However, I think we can use the same trick that worked for the static toBuilder method: cast to a common interface, then call a method (say, mergeInto) that has different implementations in the Value and Partial types. If the cast fails, fall back to the old code path. This avoids a performance hit because hotspot will determine a single type is used in practice and optimize for it.
One risk in doing this is that people might start using partials in production code for this behaviour. We would need to document the preferred alternative (namely, merge from a partially filled Builder).
This still doesn't make partiality "stick" to the builder in the same way it does for toBuilder, though that also doesn't happen if you use mergeFrom(partial.toBuilder()). It may still make testing sufficiently easier that it is worth doing.