Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Switching from Java to Kotlin

Shohei Kawano
November 06, 2017

Switching from Java to Kotlin

While 'Kotlin is cute' and I strongly agree with it, I faced some obstacles or unexpected results while converting Java source code from my project into Kotlin.

In this talk I showed some of the real examples of unexpected or surprising things I faced while converting the project from Java source code into Kotlin source code, and also what I think is important when you start converting your project from Java to Kotlin, which is reviewing decompiled byte code.

See also:
* Exploring Kotlin’s hidden costs — Part 1 – Christophe Beyls – Medium
https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-1-fbb9935d9b62

* Exploring Kotlin’s hidden costs — Part 2 – Christophe Beyls – Medium
https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-2-324a4a50b70

* Exploring Kotlin’s hidden costs — Part 3 – Christophe Beyls – Medium
https://medium.com/@BladeCoder/exploring-kotlins-hidden-costs-part-3-3bf6e0dbf0a4

* Kotlin's hidden costs - Benchmarks - Renato Athaydes
https://sites.google.com/a/athaydes.com/renato-athaydes/posts/kotlinshiddencosts-benchmarks

Keynote: Sinking Your Teeth Into Bytecode | SkillsCast | 26th October 2017
https://skillsmatter.com/skillscasts/10012-a-talk-by-jake-wharton

Shohei Kawano

November 06, 2017
Tweet

More Decks by Shohei Kawano

Other Decks in Technology

Transcript

  1. This talk… ‣is introductory ☺ ‣shows some real obstacles I

    faced when I was converting Java -> Kotlin ‣Android Studio 3.0 / Kotlin: 1.1.51
  2. Before release: ~2017.08 ‣☺ 1 Android developer ‣ Develop for

    initial release!! ‣ Google I/O 2017 ‣ Fully Java project
  3. After Release: 2017.09~ ‣☺ 1 Android developer ‣ Feature Dev

    Release Java -> kt Feature Dev Release Java -> kt Feature Dev Release Java -> kt Feature Dev Release Java -> kt
  4. Things I Struggled * Or things that may need to

    be in consideration when converting source code from Java to Kotlin
  5. onActivityResult override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {

    super.onActivityResult(requestCode, resultCode, data) ActivityResult.onResult(requestCode, resultCode, data).into(this) }
  6. onActivityResult override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {

    super.onActivityResult(requestCode, resultCode, data) ActivityResult.onResult(requestCode, resultCode, data).into(this) }
  7. onActivityResult override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {

    super.onActivityResult(requestCode, resultCode, data) ActivityResult.onResult(requestCode, resultCode, data).into(this) }
  8. onActivityResult override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    super.onActivityResult(requestCode, resultCode, data) ActivityResult.onResult(requestCode, resultCode, data).into(this) } ☺
  9. Support Library 27.0.0 ‣support.v4.app.Fragment API annotated with @Nullable ✨ @Nullable

    public final Bundle getArguments() { return this.mArguments; } @Nullable public Context getContext() { return this.mHost == null?null:this.mHost.getContext(); } @Nullable public final FragmentActivity getActivity() { return this.mHost == null?null:(FragmentActivity)this.mHost.getActivity(); }
  10. import android.support.v4.app.Fragment fun Fragment.argsOrThrow() = arguments ?: throw IllegalStateException("arguments are

    null") fun Fragment.contextOrThrow() = context ?: throw IllegalStateException("context is null") fun Fragment.activityOrThrow() = activity ?: throw IllegalStateException("activity is null") Option #2: Extension Function
  11. import android.support.v4.app.Fragment fun Fragment.argsOrThrow() = arguments ?: throw IllegalStateException("arguments are

    null") fun Fragment.contextOrThrow() = context ?: throw IllegalStateException("context is null") fun Fragment.activityOrThrow() = activity ?: throw IllegalStateException("activity is null") Option #2: Extension Function Or more?
  12. Data Binding private val binding by lazy { DataBindingUtil.setContentView <ActivityHomeBinding>(this,

    R.layout.activity_home) } binding.button.setOnClickListener { someAction.doAction() }
  13. Data Binding private val binding by lazy { DataBindingUtil.setContentView <ActivityHomeBinding>(this,

    R.layout.activity_home) } binding.button.setOnClickListener { someAction.doAction() }
  14. Data Binding <?xml version="1.0" encoding=“utf-8"?> <layout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" >

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=“Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> …
  15. <?xml version="1.0" encoding=“utf-8"?> <layout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=“Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> … Data Binding activity_home.xml
  16. Data Binding private val binding by lazy { DataBindingUtil.setContentView <ActivityHomeBinding>(this,

    R.layout.activity_home) } binding.button.setOnClickListener { someAction.doAction() }
  17. Data Binding public class ActivityHomeBinding extends android.databinding.ViewDataBinding { @Nullable private

    static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes; @Nullable private static final android.util.SparseIntArray sViewsWithIds; static { sIncludes = null; … } }
  18. Data Binding public class ActivityHomeBinding extends android.databinding.ViewDataBinding { @Nullable private

    static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes; @Nullable private static final android.util.SparseIntArray sViewsWithIds; static { sIncludes = null; … } } ActivityHomeBinding.java
  19. Data Binding public class ActivityHomeBinding extends android.databinding.ViewDataBinding { @Nullable private

    static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes; @Nullable private static final android.util.SparseIntArray sViewsWithIds; static { sIncludes = null; … } } Does anyone know how to jump to .xml file?
  20. ‣ProGuard Enabled for Release build ‣MultiDex Enabled only for Debug

    build ‣Json Parser: Moshi → Add moshi-kotlin extension
  21. @Metadata( mv = {1, 1, 7}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\u0018\u0000 \u00072\u00020\u0001:\u0001\u0007B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\b"}, d2 = {"Lcom/shaunkawano/androidkotlinsimpleoverheadsample/MainActivity;", "Landroid/support/v7/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/ Bundle;", "Companion", "production sources for module app"} ) public final class MainActivity extends AppCompatActivity { public static final int SOME_VALUE = 1; public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2); } public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } @Metadata( mv = {1, 1, 7}, bv = {1, 0, 2}, k = 1, d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0003\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢ \u0006\u0002\u0010\u0002R\u0014\u0010\u0003\u001a\u00020\u0004X\u0086D¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006¨\u0006\u0007"}, d2 = {"Lcom/shaunkawano/androidkotlinsimpleoverheadsample/MainActivity$Companion;", "", "()V", "SOME_VALUE", "", "getSOME_VALUE", "()I", "production sources for module app"} ) public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } } Decompiled Bytecode
  22. class MainActivity : AppCompatActivity() { companion object { val SOME_VALUE

    = 1 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("$SOME_VALUE") } }
  23. class MainActivity : AppCompatActivity() { companion object { val SOME_VALUE

    = 1 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("$SOME_VALUE") } } Tools/Kotlin/
 ‘Show Kotlin Bytecode’
  24. class MainActivity : AppCompatActivity() { companion object { val SOME_VALUE

    = 1 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) println("$SOME_VALUE") } } Tools/Kotlin/
 ‘Show Kotlin Bytecode’ (Configure own shortcut!)
  25. @Metadata( mv = {1, 1, 7}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\u0018\u0000 \u00072\u00020\u0001:\u0001\u0007B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\b"}, d2 = {"Lcom/shaunkawano/androidkotlinsimpleoverheadsample/MainActivity;", "Landroid/support/v7/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/ Bundle;", "Companion", "production sources for module app"} ) public final class MainActivity extends AppCompatActivity { public static final int SOME_VALUE = 1; public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2); } public View _$_findCachedViewById(int var1) { if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { var2 = this.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } public void _$_clearFindViewByIdCache() { if(this._$_findViewCache != null) { this._$_findViewCache.clear(); } } @Metadata( mv = {1, 1, 7}, bv = {1, 0, 2}, k = 1, d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0003\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢ \u0006\u0002\u0010\u0002R\u0014\u0010\u0003\u001a\u00020\u0004X\u0086D¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006¨\u0006\u0007"}, d2 = {"Lcom/shaunkawano/androidkotlinsimpleoverheadsample/MainActivity$Companion;", "", "()V", "SOME_VALUE", "", "getSOME_VALUE", "()I", "production sources for module app"} ) public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }
  26. public final class MainActivity extends AppCompatActivity { public static final

    int SOME_VALUE = 1; public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2); } public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }
  27. public final class MainActivity extends AppCompatActivity { public static final

    int SOME_VALUE = 1; public static final MainActivity.Companion Companion = new MainActivity.Companion((DefaultConstructorMarker)null); private HashMap _$_findViewCache; protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296283); String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2); } public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }
  28. companion object { val SOME_VALUE = 1 } println("$SOME_VALUE") public

    static final int SOME_VALUE = 1; public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } } … String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2);
  29. companion object { val SOME_VALUE = 1 } println("$SOME_VALUE") public

    static final int SOME_VALUE = 1; public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } } … String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2);
  30. companion object { val SOME_VALUE = 1 } println("$SOME_VALUE") public

    static final int SOME_VALUE = 1; public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } } … String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2);
  31. companion object { val SOME_VALUE = 1 } println("$SOME_VALUE") public

    static final int SOME_VALUE = 1; public static final class Companion { public final int getSOME_VALUE() { return MainActivity.SOME_VALUE; } } … String var2 = "" + Companion.getSOME_VALUE(); System.out.println(var2); ‘const’ keyword
  32. companion object { const val SOME_VALUE = 1 } println(“$SOME_VALUE")

    public static final int SOME_VALUE = 1; public static final class Companion { … } String var2 = "1"; System.out.println(var2);
  33. companion object { const val SOME_VALUE = 1 } println(“$SOME_VALUE")

    public static final int SOME_VALUE = 1; public static final class Companion { … } String var2 = "1"; System.out.println(var2); For non-const property?
  34. companion object { const val SOME_VALUE = 1 } println(“$SOME_VALUE")

    public static final int SOME_VALUE = 1; public static final class Companion { … } String var2 = "1"; System.out.println(var2); @JvmField
  35. Decompiled Bytecode ‣Shows you how your Kotlin code will look

    as in Java ‣Enables you to check if you were writing something you would never write in Java ‣There are Pros/Cons; talk to your teammate
  36. Conclusion ‣No rush when converting ‣Review your decompiled byte code

    ‣Always doubt; never blind yourself with the phrase ‘Kotlin is cute’