I'm working on some android project where I'm mixing Java and Kotlin. I have a piece of Kotlin code that I decompiled in Java to see how it is actually converted.
Kotlin code
fun postSettingToServer() {
val request = CoockieJsonRequest(Request.Method.POST, URLBuilder.GetPushSettings(this), pushModel!!.toJSON(), null, null)
VolleySingleton.getInstance(applicationContext).addToRequestQueue(request)
}
Android studio created Java equivalent
public final void postSettingToServer() {
CoockieJsonRequest var10000 = new CoockieJsonRequest;
String var10003 = URLBuilder.GetPushSettings((Context)this);
Intrinsics.checkExpressionValueIsNotNull(var10003, "URLBuilder.GetPushSettings(this)");
PushSettings var10004 = this.pushModel;
if (var10004 == null) {
Intrinsics.throwNpe();
}
var10000.<init>(1, var10003, var10004.toJSON(), (Listener)null, (ErrorListener)null);
CoockieJsonRequest request = var10000;
VolleySingleton.getInstance(this.getApplicationContext()).addToRequestQueue((Request)request);
}
What bothers me is this CoockieJsonRequest var10000 = new CoockieJsonRequest;
. So, basically, here we can see that the code allocated the memory to CoockieJsonRequest
with new
operator, but don't call it constructor(no braces). Instead of this, the code does some other operations(unwrapping pushModel
object) and only then initializes CoockieJsonRequest
using JVM <init>
. This looks really weird to me since I always thought that the object has to be constructed when it is allocated.
So, my questions - it is how it works (construction can be deferred) or something is wrong with Android Studion Kotlin decompiler and it just creates weird decompile outputs?
TL;DR: It's nearly a normal instance construction, but the conditionals confuse the decompiler.
At bytecode level, it's two different instructions. new
creates an uninitialized instance of the given class. invokespecial
, referencing the <init>
method, initializes the instance using the given constructor. By no means is it obligatory that they immediately follow one another.
The code you're seeing roughly matches the following java code (after inlining the synthetic variables):
CoockieJsonRequest request = new CoockieJsonRequest(1,
URLBuilder.GetPushSettings((Context)this),
this.pushModel.toJSON(),
(Listener) null,
(ErrorListener) null);
It seems to be quite normal that Java compilers
new
instruction,<init>
method using the invokespecial
instruction.So, with a Java compiler, the Java instance creation from above will probably produce a very similar bytecode sequence, hence a similar decompilation.
Only the Intrinsics
null checks make a difference in the bytecode, and probably confuse the decompiler enough so that it couldn't inline the argument expressions into a "normal" constructor invocation.
E.g. when trying to inline the var10004
expression, the if
construct could at best be replaced with a ternary operator, making the resulting Java code at least complicated, if not impossible. So, it's quite plausible that the decompiler fails here.