[Kotlin] Kotlin default value support in CompatibleMode
Feature Request
Kotlin support default value for fields, when deserializing, fury should assign default value to field if serialization didn' have that field and not write value.
For example, this is the class when serialization
data class Foo(val s: String)
val foo = Foo("abc")
And this is the class when deserialization.
data class Foo(val int: Int = 10, val s: String)
If we deserialize the data from data class Foo(val s: String), we will get Foo(int=0, s=abc), but we should return Foo(int=10, s=abc)
Is your feature request related to a problem? Please describe
Serialization:
data class Foo(val s: String )
fun main(args: Array<String>) {
val fury = Fury.builder().requireClassRegistration(false).withCompatibleMode(CompatibleMode.COMPATIBLE).build()
Files.write(Paths.get("data"), fury.serialize(Foo(s = "abc")))
}
Deserialization:
data class Foo(val int: Int = 10, val s: String )
fun main(args: Array<String>) {
val fury = Fury.builder().requireClassRegistration(false).withCompatibleMode(CompatibleMode.COMPATIBLE).build()
println(fury.deserialize(Files.readAllBytes(Paths.get("data"))))
// Foo(int=0, s=abc)
}
Describe the solution you'd like
Here is disassembled java code:
public final class Foo {
private final int int;
@NotNull
private final String s;
public Foo(int n, @NotNull String s) {
Intrinsics.checkNotNullParameter((Object)s, (String)"s");
this.int = n;
this.s = s;
}
public /* synthetic */ Foo(int n, String string, int n2, DefaultConstructorMarker defaultConstructorMarker) {
if ((n2 & 1) != 0) {
n = 10;
}
this(n, string);
}
public final int getInt() {
return this.int;
}
@NotNull
public final String getS() {
return this.s;
}
public final int component1() {
return this.int;
}
@NotNull
public final String component2() {
return this.s;
}
@NotNull
public final Foo copy(int n, @NotNull String s) {
Intrinsics.checkNotNullParameter((Object)s, (String)"s");
return new Foo(n, s);
}
public static /* synthetic */ Foo copy$default(Foo foo, int n, String string, int n2, Object object) {
if ((n2 & 1) != 0) {
n = foo.int;
}
if ((n2 & 2) != 0) {
string = foo.s;
}
return foo.copy(n, string);
}
@NotNull
public String toString() {
return "Foo(int=" + this.int + ", s=" + this.s + ')';
}
public int hashCode() {
int result = Integer.hashCode(this.int);
result = result * 31 + this.s.hashCode();
return result;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Foo)) {
return false;
}
Foo foo = (Foo)other;
if (this.int != foo.int) {
return false;
}
return Intrinsics.areEqual((Object)this.s, (Object)foo.s);
}
}
Kotlin generated a constructor for default value:
public /* synthetic */ Foo(int n, String string, int n2, DefaultConstructorMarker defaultConstructorMarker) {
if ((n2 & 1) != 0) {
n = 10;
}
this(n, string);
}
But it didn't provide this default value as a field, so we can't extract this value using reflection.
We could create a Foo object and check fields values:
- If primitive fields value doesn't equal to java primitive value, then it's a default value
- If object fields value doesn't equal to
null, then it's a default value
And when deserializiing, use the computed default value to set such fields.
For codegen, wen need to extend MetaSharedCodecBuilder. When codegen disabled, MetaSharedSerializer needs to be extended
Describe alternatives you've considered
No response
Additional context
https://github.com/apache/fury/issues/1683
Hi @wywen, are you interested in this issue?