Slide 1

Slide 1 text

Switching from Java to Kotlin shaunkawano

Slide 2

Slide 2 text

This talk… ‣is introductory ☺ ‣shows some real obstacles I faced when I was converting Java -> Kotlin ‣Android Studio 3.0 / Kotlin: 1.1.51

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

https://speakerdeck.com/ shaunkawano

Slide 6

Slide 6 text

Before release: ~2017.08 ‣☺ 1 Android developer ‣ Develop for initial release!! ‣ Google I/O 2017

Slide 7

Slide 7 text

Before release: ~2017.08 ‣☺ 1 Android developer ‣ Develop for initial release!! ‣ Google I/O 2017 ‣ Fully Java project

Slide 8

Slide 8 text

After Release: 2017.09~ ‣☺ 1 Android developer ‣

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Code/ ‘Convert Java File to Kotlin File’ (Mac: ⌘ Option Shift K )

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Things I Struggled

Slide 14

Slide 14 text

Things I Struggled * Or things that may need to be in consideration when converting source code from Java to Kotlin

Slide 15

Slide 15 text

1. Null Safety

Slide 16

Slide 16 text

e.g. onActivityResult

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

e.g. support.v4.app.Fragment

Slide 22

Slide 22 text

Support Library 27.0.0 ‣support.v4.app.Fragment API annotated with @Nullable ✨

Slide 23

Slide 23 text

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(); }

Slide 24

Slide 24 text

Option #1: ‘!!’ fragment.arguments!! fragment.context!! fragment.activity!!

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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?

Slide 27

Slide 27 text

2. Code Diversion

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Data Binding …

Slide 31

Slide 31 text

… Data Binding activity_home.xml

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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; … } }

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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?

Slide 36

Slide 36 text

3. Method Counts

Slide 37

Slide 37 text

Build/ ‘Analyze APK…’ Check what’s inside classes.dex

Slide 38

Slide 38 text

kotlin package ‣ProGuarded ‣Defined methods: 259 ‣Referenced methods: 293

Slide 39

Slide 39 text

‣ProGuard Enabled for Release build ‣MultiDex Enabled only for Debug build ‣Json Parser: Moshi


Slide 40

Slide 40 text

moshi + kotlin

Slide 41

Slide 41 text

moshi + kotlin

Slide 42

Slide 42 text

moshi + kotlin If you never annotate @Json,

Slide 43

Slide 43 text

moshi + kotlin ✅ ‘com.squareup.moshi:moshi:1.5.0' If you never annotate @Json,

Slide 44

Slide 44 text

moshi + kotlin If you annotate @Json,

Slide 45

Slide 45 text

moshi + kotlin If you annotate @Json, ✅ ‘com.squareup.moshi:moshi-kotlin:1.5.0’

Slide 46

Slide 46 text

‣ProGuard Enabled for Release build ‣MultiDex Enabled only for Debug build ‣Json Parser: Moshi


Slide 47

Slide 47 text

‣ProGuard Enabled for Release build ‣MultiDex Enabled only for Debug build ‣Json Parser: Moshi → Add moshi-kotlin extension

Slide 48

Slide 48 text

Build/ ‘Analyze APK…’ Check what’s inside classes.dex

Slide 49

Slide 49 text

kotlin package ‣ProGuarded ‣Defined methods: 9632 ‣Referenced methods: 10396

Slide 50

Slide 50 text

Dex Limit : 65536 Methods

Slide 51

Slide 51 text

Unable to execute dex:method ID not in [0, 0xfffff]: 65536

Slide 52

Slide 52 text

‘Compare with previous APK…’

Slide 53

Slide 53 text

Things I Care

Slide 54

Slide 54 text

@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

Slide 55

Slide 55 text

e.g. Companion Object

Slide 56

Slide 56 text

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") } }

Slide 57

Slide 57 text

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’

Slide 58

Slide 58 text

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!)

Slide 59

Slide 59 text

companion object { val SOME_VALUE = 1 } println("$SOME_VALUE")

Slide 60

Slide 60 text

@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(); } } }

Slide 61

Slide 61 text

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(); } } }

Slide 62

Slide 62 text

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(); } } }

Slide 63

Slide 63 text

companion object { val SOME_VALUE = 1 } println("$SOME_VALUE")

Slide 64

Slide 64 text

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);

Slide 65

Slide 65 text

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);

Slide 66

Slide 66 text

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);

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

‘const’ keyword ‣Compile-Time-Constants ‣String or Primitive of top-level or members of object ‣No ‘getter’ to be generated

Slide 69

Slide 69 text

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);

Slide 70

Slide 70 text

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?

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

@JvmField ‣Tell Kotlin compiler not to generate setter/getter ‣Expose property as a field in Java

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

Conclusion ‣No rush when converting ‣Review your decompiled byte code ‣Always doubt; never blind yourself with the phrase ‘Kotlin is cute’

Slide 75

Slide 75 text

shaunkawano Switching from Java to Kotlin