How to generate serialVersionUID for kotlin exceptions? - serialization

As kotlin doesn't have static fields, should I define serialVersionUID in companion object?

To create the serialVersionUID for a class in Kotlin you have a few options all involving adding a member to the companion object of the class.
The most concise bytecode comes from a private const val which will become a private static variable on the containing class, in this case MySpecialCase:
class MySpecialCase : Serializable {
companion object {
private const val serialVersionUID: Long = 123
}
}
You can also use these forms, each with a side effect of having getter/setter methods which are not necessary for serialization...
class MySpecialCase : Serializable {
companion object {
private val serialVersionUID: Long = 123
}
}
This creates the static field but also creates a getter as well getSerialVersionUID on the companion object which is unnecessary.
class MySpecialCase : Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 123
}
}
This creates the static field but also creates a static getter as well getSerialVersionUID on the containing class MySpecialCase which is unnecessary.
But all work as a method of adding the serialVersionUID to a Serializable class.

Yes, you can declare it in the companion object. The doc says:
Also, public properties defined in objects and companion objects, as well as top-level properties annotated with const, are turned into static fields in Java
But that seems to be the case with private properties too:
class MyException: Exception() {
companion object {
private val serialVersionUid: Long = 1
}
}
javap -c -p com.ninja_squad.kotlindiscovery.MyException.class
Compiled from "MyException.kt"
public final class com.ninja_squad.kotlindiscovery.MyException extends java.lang.Exception {
private static final long serialVersionUid;
public static final com.ninja_squad.kotlindiscovery.MyException$Companion Companion;
static {};
Code:
0: getstatic #38 // Field com/ninja_squad/kotlindiscovery/MyException$Companion.INSTANCE:Lcom/ninja_squad/kotlindiscovery/MyException$Companion;
3: putstatic #40 // Field Companion:Lcom/ninja_squad/kotlindiscovery/MyException$Companion;
6: lconst_1
7: putstatic #21 // Field serialVersionUid:J
10: return
public com.ninja_squad.kotlindiscovery.MyException();
Code:
0: aload_0
1: invokespecial #15 // Method java/lang/Exception."<init>":()V
4: return
public static final long access$getSerialVersionUid$cp();
Code:
0: getstatic #21 // Field serialVersionUid:J
3: lreturn
}

You can use this plugin GenerateSerialVersionUID to automatically generate the serialVersionUID field.
Since Kotlin language does not support static, you need to use the const decoration in the generated companion object (of course, it is also possible to use the #JvmField annotation).

Related

Kotlin allows the same function signature as property getter with different return type

Update 2020-12-23
The origin description is a little bit confusing. Kotlin not just allows same signature with getter in sub-class but also in self class too. So this is also allowed:
open class BaseRequest {
val params = mutableMapOf<String, String>()
fun getParams(): List<String> {
return params.values.toList()
}
}
As #Slaw said, this is the behaviour of kotlin compilor and it works since JVM invoke the correct method using address but not "the signature".
I run into a situation that seems Kotlin allows sub-class create the same signature as super-class’s getter.
Generally, functions has same signature and different return type is not allowed. So I’m confused about this situation. I’m not sure whether this is by designed.
Here is a sample:
open class BaseRequest {
val params = mutableMapOf<String, String>()
init {
params["key1"] = "value1"
}
}
class SpecificRequest : BaseRequest() {
init {
params["key2"] = "value2"
}
fun getParams(): List<String> {
return params.values.toList()
}
}
MediatorRequest has a function getParams() which has same signature as it’s super-class but has different return type. While using this function, it seems the sub class and super class has different implement of the same declaration.
fun main() {
val specificRequest = SpecificRequest()
println("specificRequest.params: ${specificRequest.params}")
println("specificRequest.getParams(): ${specificRequest.getParams()}")
println("(specificRequest as BaseRequest).params: ${(specificRequest as BaseRequest).params}")
}
The Output would be like this:
specificRequest.params: {key1=value1, key2=value2}
specificRequest.getParams(): [value1, value2]
(specificRequest as BaseRequest).params: {key1=value1, key2=value2}
If we look at the decompiled Java code, there are two methods has same signature and different return type and this is truly not allowed in Java.
public class BaseRequest {
#NotNull
private final Map params;
#NotNull
public final Map getParams() {
return this.params;
}
/* ... */
}
public final class SpecificRequest extends BaseRequest {
#NotNull
public final List getParams() {
return CollectionsKt.toList((Iterable)this.getParams().values());
}
/* ... */
}
I know the function name is not appropriate but there is a potential risk that if we use the SpecificRequest in .java, we can not visite the Map params until we cast the instance to it’s super class. And this may lead to misunderstanding.
There is a difference between Java the language and the JVM. The Java language does not allow two methods with the same name but different return types to be declared in the same class. This is a restriction of the language. The JVM, however, is perfectly capable of distinguishing between the two methods. And since Kotlin is its own language it does not necessarily have to follow the exact same rules as Java—even when targeting the JVM (and thus compiled to byte-code).
Consider the following Kotlin class:
class Foo {
val bar = mapOf<Any, Any>()
fun getBar() = listOf<Any>()
}
If you compile the class and then inspect the byte-code with javap you'll see:
Compiled from "Foo.kt"
public final class Foo {
public final java.util.Map<java.lang.Object, java.lang.Object> getBar();
public final java.util.List<java.lang.Object> getBar();
public Foo();
}
So the two functions definitely exist, despite having the same name. But if you access the property and call the function you'll see that:
fun test() {
val foo = Foo()
val bar1 = foo.bar
val bar2 = foo.getBar()
}
Becomes:
public static final void test();
descriptor: ()V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=3, args_size=0
0: new #8 // class Foo
3: dup
4: invokespecial #11 // Method Foo."<init>":()V
7: astore_0
8: aload_0
9: invokevirtual #15 // Method Foo.getBar:()Ljava/util/Map;
12: astore_1
13: aload_0
14: invokevirtual #18 // Method Foo.getBar:()Ljava/util/List;
17: astore_2
18: return
Which shows the byte-code knows which function to call. And the JVM can handle this.
But there is a caveat. The following will fail to compile:
class Foo {
fun getBaz() = mapOf<Any, Any>()
fun getBaz() = listOf<Any>()
}
Why? I'm not positive, but I believe it has to do with the syntax. The Kotlin compiler can always easily tell which function you meant to invoke based on if you used foo.bar or foo.getBar(). But the syntax is the same for calling the two getBaz() functions, which means the compiler can't easily tell which function you meant to invoke in all cases (and so it disallows the above).

Gson - deserialize or default

I have a class :
data class Stam(#SerializedName("blabla") val blabla: String = "")
I want to do gson.fromJson("{\"blabla\":null}", Stam::class.java)
However, it will fail because blabla is not nullable.
I want to make it so if gson failed to deserialize some variable, it will take the default value I give it.
How to achieve that?
I don't think it is possible with GSON, this is one of the reasons why kotlinx.serialization library was created. With this library it is fairly easy:
#Serializable
data class Stam(#SerialName("blabla") val blabla: String = "") //actually, #SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{\"blabla\":null}")
I wouldn't say it is not possible in Gson, but Gson is definitely not the best choice:
Gson has no mention on Kotlin, its runtime and specifics, so one is better to use a more convenient and Kotlin-aware tool. Typical questions here are: how to detect a data class (if it really matters, can be easily done in Kotlin), how to detect non-null parameters and fields in runtime, etc.
Data classes in Kotlin seem to provide a default constructor resolvable by Gson therefore Gson can invoke it (despite it can instantiate classes instances without constructors using unsafe mechanics) delegating to the "full-featured" constructor with the default arguments. The trick here is removing null-valued properties from input JSON so Gson would keep "default-argumented" fields unaffected.
I do Java but I do believe the following code can be converted easily (if you believe Gson is still a right choice):
final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
#Override
#Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
#Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}
Test:
public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
#Test
public void test() {
final Stam stam = gson.fromJson("{\"blabla\":null}", Stam.class);
// The test is "green" since
Assertions.assertEquals("", stam.getBlabla());
}
}
I still think Gson is not the best choice here.

Kotlin data class with different backing field type

I have a simple class used for JSON serialization. For this purpose, the external interface uses Strings, but the internal representation is different.
public class TheClass {
private final ComplexInfo info;
public TheClass(String info) {
this.info = new ComplexInfo(info);
}
public String getInfo() {
return this.info.getAsString();
}
// ...more stuff which uses the ComplexInfo...
}
I have this working in Kotlin (not sure if there's a better way). But the non-val/var constructor prevents me from using data.
/*data*/ class TheClass(info: String) {
private val _info = ComplexInfo(info)
val info: String
get() = _info.getAsString()
// ...more stuff which uses the ComplexInfo...
}
How do I get this working as a data class?
You can use a combination of a private ComplexInfo property declared in the primary constructor and a secondary constructor that accepts a String.
Optionally, make the primary constructor private.
Example:
data class TheClass private constructor(private val complexInfo: ComplexInfo) {
constructor(infoString: String) : this(ComplexInfo(infoString))
val info: String get() = complexInfo.getAsString()
}
Note that it's the complexInfo property that is used in the data class generated members implementations.

How to access static fields with reflect in kotlin?

I have this abstract class in java:
abstract class AbsApiTestCase<T> {
T mApi;
#Before
public void setUp() throws Exception {
mApi = instanceApi((Class<T>) (
(ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]);
}
static <T> T instanceApi(Class<T> clazz) throws Exception {
return new Retrofit.Builder()
.baseUrl(clazz.getField("BASE_URL").get(null).toString())
.addConverterFactory(GsonConverterFactory.create(
new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getClient())
.build().create(clazz);
}
// some code
}
And api looks like this:
public interface GithubApi {
String BASE_URL = "https://api.github.com/";
// some code
}
It can be used like this:
public class GithubApiTest extends AbsApiTestCase<GithubApi> {
// some code
}
But when I convert my code to kotlin, the static field BASE_URL looks like this:
interface GithubApi {
companion object {
val BASE_URL = "https://api.github.com/"
}
// some code
}
And BASE_URL cannot be accessed like above. I found there is a #JvmField annotation but Android studio says JvmField cannot be applied to a property defined in companion object of interface.
Is there a way to access this "static field"?
How about making BASE_URL a compile-time constant?
interface GithubApi {
companion object {
const val BASE_URL = "https://api.github.com/"
}
}
At byte-code level BASE_URL is a static field of the GithubApi interface.
public interface GithubApi {
public static final GithubApi$Companion Companion;
public static final java.lang.String BASE_URL;
static {};
Code:
0: new #26 // class GithubApi$Companion
3: dup
4: aconst_null
5: invokespecial #30 // Method GithubApi$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
8: putstatic #32 // Field Companion:LGithubApi$Companion;
11: return
}
The #JvmStatic annotation will make the property's backing field a static one. That is, if the annotation is applied to a property within a companion object, then a new static field will be created in the enclosing class.
Note that Kotlin really has no notion of static, and that this annotation is merely for accessibility accross JVM languages.
I see four basic options for that:
1) Extract the property into an object (companion or not):
object GithubApiUrls {
val BASE_URL = "https://api.github.com/"
}
2) Make a package-level property:
package myProgram
val BASE_URL = "https://api.github.com/"
3) Use a class, not an interface, if you need inheritance (no idea what for, TBH):
open class GithubApi {
val BASE_URL = "https://api.github.com/"
}
4) Replace the field with a method in the interface (can be overridden):
interface GithubApi {
fun BaseUrl() = "https://api.github.com/"
}
Wouldn't suggest putting constants in interfaces at all. It introduces too much complexity without real value gain.
Try decoupling class that holds the constant from the actual class that implements the interface.
public class AllUrls {
public static final String GITHUB_URL = "https://api.github.com/";
}
That will become
object AllUrls {
val GITHUB_URL = "https://api.github.com/"
}
And to use it
static <T> T instanceApi(Class<T> clazz) throws Exception {
return new Retrofit.Builder()
.baseUrl(AllUrls.INSTANCE.getGITHUB_URL())
.addConverterFactory(GsonConverterFactory.create(
new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getClient())
.build().create(clazz);
}

Static data in Kotlin

Please tell me, is there any difference (in terms of Java) in this examples:
object DefaultValues {
val FILES_TO_DOWNLOAD = 100
}
and
class DefaultValues {
companion object {
val FILES_TO_DOWNLOAD = 100
}
}
Without class or object wrapper:
const val DEFAULT_FILES_TO_DOWNLOAD = 100
and
val DEFAULT_FILES_TO_DOWNLOAD = 100
What is the true way to define?:
public static final int FILES_TO_DOWNLOAD = 100
You can use Kotlin bytecode viewer to find out what these options are compiled to.
With Kotlin 1.0.2 the compiled bytecode shows that
val property in object or companion object is compiled into a private static final field inside the class:
// access flags 0x1A
private final static I FILES_TO_DOWNLOAD = 100
and a getter, which is called when referring to the property:
// access flags 0x1019
public final static synthetic access$getFILES_TO_DOWNLOAD$cp()I
From Java, the getter can be called as DefaultValues.INSTANCE.getFILES_TO_DOWNLOAD() or DefaultValues.Companion.getFILES_TO_DOWNLOAD() respectively.
Non-const top level property is compiled to the same to (1) with only difference that the field and getter are placed inside FilenameKt class now.
But top level const val is compiled into a public static final field:
// access flags 0x19
public final static I DEFAULT_FILES_TO_DOWNLOAD = 100
The same public static final field will be produced when a const val is declared inside an object. Also, you can achieve the same resulting bytecode if you add #JvmField annotation to the properties declared in (1).
Concluding that, you can define public static final field using const or #JvmField either in an object or at top level.