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

From Java to Kotlin - real project case study

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →
Avatar for Adam Styrc Adam Styrc
October 11, 2017

From Java to Kotlin - real project case study

Comparison of Java and Kotlin code after real project refatoring. Highlighting Kotlin features that worked great and bad.

Avatar for Adam Styrc

Adam Styrc

October 11, 2017

Other Decks in Programming

Transcript

  1. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null public class SocketsActivity extends Activity { private List<Socket> sockets;
  2. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null public class SocketsActivity extends Activity { private List<Socket> sockets;
  3. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null val handler = Handler() val handlerCallbacks = ArrayList<Runnable>() public class SocketsActivity extends Activity { private List<Socket> sockets; private final Handler handler = new Handler(); private final ArrayList<Runnable> handlerCallbacks = new ArrayList<>() ;
  4. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null val handler = Handler() val handlerCallbacks = ArrayList<Runnable>() @Inject lateinit var chargingStationController : ChargingStationController @Inject lateinit var chargingSessionController : ChargingSessionController public class SocketsActivity extends Activity { private List<Socket> sockets; private final Handler handler = new Handler(); private final ArrayList<Runnable> handlerCallbacks = new ArrayList<>() ; @Inject ChargingStationController chargingStationController ; @Inject ChargingSessionController chargingSessionController ;
  5. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null val handler = Handler() val handlerCallbacks = ArrayList<Runnable>() @Inject lateinit var chargingStationController : ChargingStationController @Inject lateinit var chargingSessionController : ChargingSessionController public class SocketsActivity extends Activity { private List<Socket> sockets; private final Handler handler = new Handler(); private final ArrayList<Runnable> handlerCallbacks = new ArrayList<>() ; @Inject ChargingStationController chargingStationController ; @Inject ChargingSessionController chargingSessionController ;
  6. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null public class SocketsActivity extends Activity { private List<Socket> sockets;
  7. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null … sockets.forEach { } public class SocketsActivity extends Activity { private List<Socket> sockets; … sockets.forEach { }
  8. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null … sockets?.forEach { } public class SocketsActivity extends Activity { private List<Socket> sockets; … if (sockets != null) { sockets.forEach { } }
  9. Kotlin Java class SocketsActivity : Activity() { private var sockets:

    List<Socket>? = null … sockets!!.forEach { } public class SocketsActivity extends Activity { private List<Socket> sockets; … sockets.forEach { }
  10. NuonMapController.java public class NuonMapController { private Context context; private Config

    config; private NuonWebService.NuonApi api; private FavoritesController favoritesController ; private FollowedController followedController ;
  11. NuonMapController.java public class NuonMapController { private Context context; private Config

    config; private NuonWebService.NuonApi api; private FavoritesController favoritesController ; private FollowedController followedController ; public NuonMapController (Context context , Config config , NuonWebService.NuonApi api , FavoritesController favoritesController , FollowedController followedController) { this.context = context; this .config = config; this .api = api; this .favoritesController = favoritesController ; this .followedController = followedController ; }
  12. NuonMapController.kt class NuonMapController( val applicationContext : Context, val api :

    NuonWebService.NuonApi , val filterController : FilterController , val config: Config, val favoritesController : FavoritesController , val followedController : FollowedController) {
  13. NuonMapController.kt class NuonMapController( val applicationContext : Context, val api :

    NuonWebService.NuonApi , val filterController : FilterController , val config: Config, val favoritesController : FavoritesController , val followedController : FollowedController, map: Map) {
  14. NuonMapController.kt class NuonMapController( val applicationContext : Context, val api :

    NuonWebService.NuonApi , val filterController : FilterController , val config: Config, val favoritesController : FavoritesController , val followedController : FollowedController, map: Map) { val myLocation: Location init { myLocation = map.getMyLocation() }
  15. NuonButton.java public class NuonButton extends RelativeLayout { public NuonButton(Context context)

    { super(context); } public NuonButton(Context context , AttributeSet attrs) { super(context, attrs); } public NuonButton(Context context , AttributeSet attrs , int defStyleAttr) { super(context, attrs, defStyleAttr) ; } Android requires 3 constructors for views
  16. NuonButton.java public class NuonButton extends RelativeLayout { public NuonButton(Context context)

    { super(context); init(); } public NuonButton(Context context , AttributeSet attrs) { super(context, attrs); init(); } public NuonButton(Context context , AttributeSet attrs , int defStyleAttr) { super(context, attrs, defStyleAttr) ; init(); } private void init() { LayoutInflater layoutInflater = LayoutInflater. from(getContext()) ; layoutInflater.inflate(R.layout. view_nuon_button, this, true ); ...
  17. NuonButton.kt class NuonButton : RelativeLayout { constructor (context: Context) :

    super(context) constructor (context: Context , attrs: AttributeSet?) : super(context, attrs) constructor (context: Context , attrs: AttributeSet? , defStyleAttr: Int) : super(context, attrs, defStyleAttr) init { val layoutInflater = LayoutInflater.from( context) layoutInflater.inflate(R.layout. view_nuon_button, this, true ) ...
  18. Default args fun refreshMap(event: Event? = null) { ... }

    refreshMap() ... refreshMap(RefreshMapEvent())
  19. Default args fun refreshMap(event: Event? = null, event2: Event? =

    null) { ... } refreshMap() ... refreshMap(RefreshMapEvent())
  20. Default args fun refreshMap(event: Event? = null, event2: Event? =

    null) { ... } refreshMap() ... refreshMap(RefreshMapEvent()) refreshMap( event2 = RefreshMapEvent())
  21. NuonButton.kt class NuonButton(context: Context , attrs: AttributeSet? = null, defStyleAttr:

    Int = 0) : RelativeLayout(context , attrs, defStyleAttr) { init { val layoutInflater = LayoutInflater.from(context) layoutInflater.inflate(R.layout. view_nuon_button, this, true ) ...
  22. FOR loop for (i in 0..(list.size-1)) { } for (i

    in list.size-1 downTo 0) { } for (i in 0..(list.size-1).reversed()) { }
  23. FOR loop for (i until 0..list.size) { list[i] } for

    (item in list) { item } list.forEach { it } list.forEach { item -> item }
  24. FOR loop for (i until 0..list.size) { list[i] } for

    (item in list) { item } list.forEach { it } list.forEach { item -> item } if (list != null) { for (i until 0..list.size) { list[i] } } if (list != null) { for (item in list) { item } } list?.forEach { it } list?.forEach { item -> item } nullable?
  25. “when” - with enum, int, String... when (plugType) { PlugType.TYPE_2_MODE_3

    -> ivPlugType2.visibility = View.VISIBLE PlugType.CHADEMO -> ivPlugChademo.visibility = View.VISIBLE PlugType.CCS -> ivPlugCcs.visibility = View.VISIBLE }
  26. “when” - “if” replacement if (loadingStationModel.getPriceSession() != null) { return

    String.format(applicationContext.getString(R.string.euro_plus_number), loadingStationModel.getPriceSession()) } else if (loadingStationModel.getPlugTypes()[0] == PlugType.CHADEMO) { return applicationContext.getString(R.string.tariff_chademo) } else { return applicationContext.getString(R.string.unknown) }
  27. “when” - “if” replacement return when { loadingStationModel.getPriceSession() != null

    -> String.format(applicationContext.getString(R.string.euro_plus_number), loadingStationModel.getPriceSession()) loadingStationModel.getPlugTypes()[0] == PlugType.CHADEMO -> applicationContext.getString(R.string.tariff_chademo) else -> applicationContext.getString(R.string.unknown) }
  28. “when” - “if” replacement return when { loadingStationModel.getPriceSession() != null

    -> String.format(applicationContext.getString(R.string.euro_plus_number), loadingStationModel.getPriceSession()) loadingStationModel.getPlugTypes()[0] == PlugType.CHADEMO -> applicationContext.getString(R.string.tariff_chademo) else -> applicationContext.getString(R.string.unknown) }
  29. “when” - type check fun trackScreen(activity: Activity) = when (activity)

    { is DashboardActivity -> { ... } is ChargingStationMessagesActivity -> { ... } else -> {} }
  30. FavoriteController.java private ArrayList<ChargingStation> mChargingStations ; public ArrayList<ChargingStation> getChargingStations () {

    if (mChargingStations == null) { String json = config.get(Config.Text.FAVORITE_STATIONS) ; if (!TextUtils. isEmpty(json)) { try { Type listType = new TypeToken<ArrayList<ChargingStation>>() { }.getType() ; new Gson().fromJson(json , listType); } catch (Exception e) { mChargingStations = new ArrayList<>() ; } } else { mChargingStations = new ArrayList<>() ; } } return mChargingStations ; }
  31. FavoriteController.kt class FavoritesController … { val favoriteStations: ArrayList<ChargingStation> by lazy

    { val json = config.get(Config.Text.FAVORITE_STATIONS) if (!TextUtils.isEmpty(json)) { try { val listType = object : TypeToken<java.util.ArrayList<ChargingStation>>(){}. type Gson().fromJson<ArrayList<ChargingStation>>(json , listType) } catch(e: Exception) { ArrayList<ChargingStation>() } } else { ArrayList() } } No “return” - it’s lamba expression!
  32. Android case 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val

    connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val networkInfo = connectivityManager.activeNetworkInfo val online = networkInfo != null && networkInfo.isConnected if (online) { … (request) … }
  33. ContextExtensions.kt fun Context.isOnline(): Boolean { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as

    ConnectivityManager val networkInfo = connectivityManager.activeNetworkInfo return networkInfo != null && networkInfo.isConnected }
  34. Android case 1 - with extension override fun onCreate(savedInstanceState: Bundle?)

    { super.onCreate(savedInstanceState) if (isOnline()) { … (request) … }
  35. Android case 2 val dp = 16 val pixels =

    dp * resources.displayMetrics.density ivSocketGreen.setPadding(pixels.toInt(), 0, 0, 0)
  36. Element exists in list? fun isStationUnderMyChargingSession(station: ChargingStation): Boolean { if

    (!isSessionStarted()) { return false } val myChargingStationPoint = chargingSession !!.chargingPoint !! val checkedStationPoints = station. chargingPoints for (currentChargingPoint in station.chargingPoints ) { if (currentChargingPoint. id == myChargingStationPoint. id) { return true } } return false }
  37. Element exists in list? - better way fun isStationUnderMyChargingSession(station: ChargingStation):

    Boolean { if (!isSessionStarted()) { return false } val myChargingStationPoint = chargingSession !!.chargingPoint !! val checkedStationPoints = station. chargingPoints return checkedStationPoints. filter { it.id == myChargingStationPoint. id }.isNotEmpty() }
  38. ProgressActivity.java public class ProgresActivity { public static ProgressActivity activity; public

    static boolean progressActive = false; public static void showProgressActivity (Context context) { if (!progressActive) { progressActive = true; Intent intent = new Intent(context , ProgressActivity. class); intent.addFlags(android.content.Intent. FLAG_ACTIVITY_NO_ANIMATION); context.startActivity(intent) ; } } public static void hideProgress () { progressActive = false; if (activity != null) { activity.finish(); }
  39. ProgressActivity.kt class ProgressActivity : Activity() { companion object { var

    activity: ProgressActivity? = null var progressActive : Boolean = false fun showProgressActivity (context: Context) { if (!progressActive ) { progressActive = true val intent = Intent(context , ProgressActivity:: class.java) intent.addFlags(android.content.Intent. FLAG_ACTIVITY_NO_ANIMATION) context.startActivity(intent) } } fun hideProgress () { progressActive = false activity?.finish() } }
  40. Problems - annotation processor (kapt) did not generate required classes

    for dependency injection (Dagger) - problems with generics implementation: new keywords: Any, *, out, in, where
  41. Conclusions 1. Way less code to write. 2. Kotlin is

    very modern language with lambdas, higher-order functions, ranges. Moves away from deprecated class inheritance. 3. Setters and getters under the hood. 4. Null references are under control with type system (?, !, !!) 5. Makes your life comfortable with extension functions! 6. Fully compatible and almost transparent with existing Java code. You can rewrite class by class! 7. You can always fall back to pure Java in case of implementation problems or compile problems!