Code review comment: tbh, I feel like the `TypeSerializer` interface could be cleaned up in this regard. Some `reuse` should be marked as `@Nullable` and each implementation should be able to handle that specific case (i.e. instantiating a new instance) rather than having two separate implementations. There would be a default implementation of `copy(T)` and `deserialize(T)` in that case. But that's out-of-scope for this PR.

Code before review:
public T deserialize(T reuse, DataInputView source) throws IOException {
int flags = source.readByte();
if ((flags & IS_NULL) != 0) {
return null;
}
Class<?> subclass = null;
TypeSerializer subclassSerializer = null;
if ((flags & IS_SUBCLASS) != 0) {
String subclassName = source.readUTF();
try {
subclass = Class.forName(subclassName, true, cl);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot instantiate class.", e);
}
subclassSerializer = getSubclassSerializer(subclass);
if (reuse == null || subclass != reuse.getClass()) {
reuse = (T) subclassSerializer.createInstance();
initializeFields(reuse);
}
} else if ((flags & IS_TAGGED_SUBCLASS) != 0) {
int subclassTag = source.readByte();
subclassSerializer = registeredSerializers[subclassTag];
if (reuse == null || ((PojoSerializer) subclassSerializer).clazz != reuse.getClass()) {
reuse = (T) subclassSerializer.createInstance();
initializeFields(reuse);
}
} else {
if (reuse == null || clazz != reuse.getClass()) {
reuse = createInstance();
}
}
if (isRecord) {
try {
JavaRecordBuilderFactory<T>.JavaRecordBuilder builder = recordHelper.newBuilder();
for (int i = 0; i < numFields; i++) {
boolean isNull = source.readBoolean();
if (fields[i] != null) {
if (isNull) {
builder.setField(i, null);
} else {
Object reuseField = reuse == null ? null : fields[i].get(reuse);
builder.setField(i, deserializeField(reuseField, i, source));
}
} else if (!isNull) {
fieldSerializers[i].deserialize(source);
}
}
reuse = builder.build();
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Error during POJO copy, this should not happen since we check the fields before.",
e);
}
} else if ((flags & NO_SUBCLASS) != 0) {
try {
for (int i = 0; i < numFields; i++) {
boolean isNull = source.readBoolean();
if (fields[i] != null) {
if (isNull) {
fields[i].set(reuse, null);
} else {
fields[i].set(reuse, deserializeField(fields[i].get(reuse), i, source));
}
} else if (!isNull) {
fieldSerializers[i].deserialize(source);
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Error during POJO copy, this should not happen since we check the fields before.",
e);
}
} else {
if (subclassSerializer != null) {
reuse = (T) subclassSerializer.deserialize(reuse, source);
}
}
return reuse;
}

Line under review: if (isRecord) {

Code after addressing the review:
public T deserialize(T reuse, DataInputView source) throws IOException {
int flags = source.readByte();
if ((flags & IS_NULL) != 0) {
return null;
}
Class<?> subclass = null;
TypeSerializer subclassSerializer = null;
if ((flags & IS_SUBCLASS) != 0) {
String subclassName = source.readUTF();
try {
subclass = Class.forName(subclassName, true, cl);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot instantiate class.", e);
}
subclassSerializer = getSubclassSerializer(subclass);
if (reuse == null || subclass != reuse.getClass()) {
reuse = (T) subclassSerializer.createInstance();
initializeFields(reuse);
}
} else if ((flags & IS_TAGGED_SUBCLASS) != 0) {
int subclassTag = source.readByte();
subclassSerializer = registeredSerializers[subclassTag];
if (reuse == null || ((PojoSerializer) subclassSerializer).clazz != reuse.getClass()) {
reuse = (T) subclassSerializer.createInstance();
initializeFields(reuse);
}
} else {
if (reuse == null || clazz != reuse.getClass()) {
reuse = createInstance();
}
}
if (isRecord()) {
try {
JavaRecordBuilderFactory<T>.JavaRecordBuilder builder = recordFactory.newBuilder();
for (int i = 0; i < numFields; i++) {
boolean isNull = source.readBoolean();
if (fields[i] != null) {
if (isNull) {
builder.setField(i, null);
} else {
Object reuseField = reuse == null ? null : fields[i].get(reuse);
builder.setField(i, deserializeField(reuseField, i, source));
}
} else if (!isNull) {
fieldSerializers[i].deserialize(source);
}
}
reuse = builder.build();
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Error during POJO copy, this should not happen since we check the fields before.",
e);
}
} else if ((flags & NO_SUBCLASS) != 0) {
try {
for (int i = 0; i < numFields; i++) {
boolean isNull = source.readBoolean();
if (fields[i] != null) {
if (isNull) {
fields[i].set(reuse, null);
} else {
fields[i].set(reuse, deserializeField(fields[i].get(reuse), i, source));
}
} else if (!isNull) {
fieldSerializers[i].deserialize(source);
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Error during POJO copy, this should not happen since we check the fields before.",
e);
}
} else {
if (subclassSerializer != null) {
reuse = (T) subclassSerializer.deserialize(reuse, source);
}
}
return reuse;
}