Upload
adit-lal
View
465
Download
6
Embed Size (px)
Citation preview
Practical tips for building apps with kotlin
Adit Lal | @aditlal
Agenda
What is Kotlin?
Perfect for Android
Migration guide
Best Practices
Summary - QnABuilding with kotlin
100 % interoperable with Java
Developed by JetBrains
Background
What is Kotlin?
Statically typed programming language for the JVM, Android and the browser
OOP language
with functional
aspects
Modern and powerful language
Safe concise & readable
code
First class tool support
What is Kotlin?
Kotlin
• Null safety
• Conciseness
• Lambdas
• Higher order functions
• Mutability protection
• Static typing + Smart Casts
We will cover :
Kotlin
Kotlin works on same byte code
Kotlin - Syntax
• Types follow variable/function names
• Functions start with fun keyword
• Default constructor in class signature
• Semicolons not required
Kotlin - Syntax
private fun sum(first: Int , second: Int): Int { return first + second }
Access modifier
Keyword
Function name
Param name Param type
Return type
Kotlin - Syntax
// Omit access modifier fun sum(a: Int , b: Int): Int { return a + b } // Inline return fun sum(a: Int , b: Int): Int = a + b
// Omit return type fun sum(a: Int , b: Int) = a + b
Kotlin - Syntax
val name: String ="John" // final
var name: String ="John" //Mutable
ex: name= "Johnny"
Two types of variables
val name ="John" // Types are auto-inferred
Kotlin - Syntax
//java String sName = person.getSecretName()
//kotlin person.secretName = “Batman” var fName = person.secretName
Kotlin - Null Safety
NULLS ARE PART OF THE TYPE SYSTEM
Kotlin - Null SafetyTypes default to non-nullable
Mark nullable with the addition of a ?
var a: String = "abc" a = null
var b: String? = "abc" b = null
var l = a.length var l = b.length
// compilation error
// OK
// error: variable 'b' can be null
Kotlin - Null Safety
Helpers
Safe accessor ?
Elvis operator ?:
val toDisplay: String = serverResponse
Kotlin - Null
// if response is null, we'll display a default
message
val toDisplay: String = response ?: defaultMessage
Kotlin - Null
// cascading null safety
val maybeName: String? = maybeNullValue?.someProperty?.name
val maybeNullValue: MyValue? = /*..*/
Kotlin - Null
// safe operation maybeNullValue?.let { //executed if not null }
val maybeNullValue: MyValue? = /*..*/
Casting
if (obj instanceOf MyType) { value = ((MyType)obj).getXValue();
} else { // handle else }
Kotlin - Smart casting
if (obj is MyType) { value = obj.x } else { // handle else }
Class
// Wont Compileclass Student : Person()
class Person
open class Person
Class
//It works nowclass Student : Person()
Higher order functions
Functions that can take functions as arguments and also return function as the
output
fun <T> forEach(list: List<T>, funcToCall: (T) -> Unit) { for (elm in list) { funcToCall(elm) }
}
val list = listOf("Alice", "Bob", "Claire") forEach(list, ::println) forEach(list, { str -> println(str) })
Higher order functions
Kotlin - Features
… and More
• Visibility Modifiers • Companion Objects • Nested, Sealed Classes • Generics • Coroutines • Operator overloading • Exceptions • Annotations • Reflection • and more
Kotlin - Features
… and Even More
• Infix extension methods • Interfaces • Interface Delegation • Property Delegation • Destructuring • Safe Singletons • Init blocks • Enums • Multiline Strings • Tail recursion
Perfect for
• Versatile language
• Complete interop with Java
• Compact runtime
• Enhance development speed with
less code
“But I’m comfortable and experienced with Java.”
Common Concerns
“I don’t want to convert my entire codebase to a new language.”
Common Concerns
•Compiled to byte code (like Java)
•No impact on performance
•Some Kotlin code faster
•Lambdas that can be inlined
•Built-in operations faster than DIY implementations
Kotlin
How to migrate
Start in small steps - Convert a Java / Android app (side project)
Try the kotlin plugin converter
Understand how code is converted from Java to Kotlin
Experiment with kotlin features and tweak code
•Add Gradle dependencies (plugin, runtime, etc.)
•Start writing .kt files instead of .java ones
•No need to migrate everything at once
•Kotlin classes can co-exist with Java ones
• IntelliJ has a Java-to-Kotlin converter
•Not perfect but good start
• Works with pasted code
Try kotlin
Java>Kotlin
class HomeActivity : AppCompatActivity() { override fun onCreate(b: Bundle?) { super.onCreate(b) setContentView(R.layout.activity_main) val view = findViewById<View>(R.id.view) view.alpha = 1f } }
Android & Kotlin
class HomeActivity : AppCompatActivity() { override fun onCreate(b: Bundle?) { super.onCreate(b) setContentView(R.layout.activity_main) val view = findViewById<View>(R.id.view) view.alpha = 1f } }
Android & Kotlin
• View binding (like Butter Knife) • No instance variables required • How?
Android extensionsapply plugin: 'kotlin-android-extensions'
• Import synthetic layout import kotlinx.android.synthetic.main..*
• Use view by ID E.g. toolbar.title = "Home"
• Under the hood: synthetic calls replaced by functions
class HomeActivity : AppCompatActivity() { override fun onCreate(b: Bundle?) { super.onCreate(b) setContentView(R.layout.activity_main) val view = findViewById<View>(R.id.view) view.alpha = 1f } }
Android & Kotlin
Android extensions
class HomeActivity : AppCompatActivity() { override fun onCreate(b: Bundle?) { super.onCreate(b) setContentView(R.layout.activity_main) val view = findViewById<View>(R.id.view) view.alpha = 1f } }
Android extensions
import kotlinx.android.synthetic.activity_main.*
class HomeActivity : AppCompatActivity() { override fun onCreate(b: Bundle?) { super.onCreate(b) setContentView(R.layout.activity_main) view.alpha = 1f } }
view.setOnClickListener{ toast(“Hello") }
Concise
view.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { toast("Hello") } })
view.setOnClickListener{ view -> doSomethingWithView() }
Concise
fun Context.inflate( res:Int, parent:ViewGroup? = null) : View {
return LayoutInflater.from(this) .inflate(res, parent, false) }
activity.inflate(R.layout.my_layout, container) //Fragment
public class Person {
private String name; private String surname; private String id;
/* Setters and getters - 20 lines */
@Override public boolean equals(Object o) { //another few lines here }
@Override public int hashCode() { //some more lines }
@Override public String toString() { //some more } }
Data classes
Kotlin -
data class Person( val name: String, var surname: String = "", val id: String )
Data classes
Kotlin - Data classes
equals() & hashCode()
toString()
copy() function
fun evaluateObject(obj: Any): String { return when(obj){ is String -> "Ahoy !!" is Int -> "Lucky no is $obj" else -> "Object is in space" } }
KotlinWhen expression
// Java if(name.toLowerCase().contains(str)) {
... }
Android
// Kotlin if (name.contains(str, true)) {
... }
Android
for (i in 1..100) { ... } for (i in 0 until 100) { ... } for (i in 2..10 step 2) { ... } for (i in 10 downTo 1) { ... } if (x in 1..10) { ... }
Android Kotlin tricks
fun ImageView.loadUrl(url: String) { Glide.with(context).load(url).into(this) }
ImageLoadingExtensions.kt
Android
//Then ivAvatar.loadUrl("some_fancy_url")
Utils
fun View.visible() { visibility = View.VISIBLE } fun View.gone() { visibility = View.GONE }
//call view.visible()
Utils
fun View.setHeight(height: Int) { val params = layoutParams params.height = height layoutParams = params }
//call tvTotalPrice.setHeight(50)
fun Activity.showOnUiThread(init: Activity.() -> Unit) : Activity { if (!isFinishing) {
runOnUiThread { init()
} } return this
}
Utils
//call showOnUiThread(updateUIWithNewData())
fun Activity.hideKeyboard(): Boolean { val view = currentFocus view?.let { val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager return imm.hideSoftInputFromWindow(view.windowToken, HIDE_NOT_ALWAYS) } return false }
Utils
Adopting kotlin into android code
class MyActivity : AppCompactActivity(){ // wont be init here lateinit var obj: CustomObject
override fun onCreate(...){ obj = intent.getExtras().get("key") // this will init the variable } }
lateinit Modifier
When to use lateinit
If you’re variable is mutable
If you’re sending a object from another component/ screen — ex Intent extras
lateinit Modifier
val myUtil by lazy { SomeUtil(parameter1, parameter2) }
Lazy Initialisation
val hashMap = HashMap<String, String>() hashMap.put("keyA", "valueA") hashMap.put("keyB", "valueB") hashMap.put("keyC", "valueC")
//change to val map = mapOf( "keyA" to "valueA", "keyB" to "valueB", "keyC" to "valueC" )
Kotlin
fun listItemsToDisplay(): List<ListItem> { return listOf(users) + friends() + recentSearches() }
Kotlin
fun parseResponse(response: Response?) = when (response?.code()) { null -> throw HTTPException(“Oh no”) 200, 201 -> parse(response.body()) in 400..499 -> throw HTTPException("Invalid request") in 500..599 -> throw HTTPException("Server error") else -> throw HTTPException("Error! Code {response.code()}”) }
Kotlin
Convert .java Automatic migration might not always result
the best code//Java private static final String MY_EXTRA=
"extra_sauce";
//Kotlin class SomeClass : AppCompatActivity() { companion object {
} //Other stuff here }
private val MY_EXTRA = “extra_sauce”
KotlinDo this instead
//Kotlin class SomeClass : AppCompatActivity() { //Other stuff here }
private val MY_EXTRA = “extra_sauce”
KotlinDo this instead
//Kotlin class SomeClass : AppCompatActivity() { //Other stuff here }
private const val MY_EXTRA = “extra_sauce”
Kotlin
class SomeClass : AppCompatActivity() { var someString = “” var someValue : Payment = /*..*/
override fun onCreate(b: Bundle?) { super.onCreate(b) if (someValue != null) someString = someValue!!.getCost() }
}
Kotlin
class SomeClass : AppCompatActivity() { var someString = “”
var someValue : Payment
override fun onCreate(b: Bundle?) { super.onCreate(b) if (someValue != null) someString = someValue!!.getCost() }
}
Kotlin
class SomeClass : AppCompatActivity() { var someString = “”
var someValue : Payment
override fun onCreate(b: Bundle?) { super.onCreate(b) someValue?.let{
someValue!!.getCost() } }
}
run
val password: Password = PasswordGenerator().run { seed = "someString" hash = {s -> someHash(s)} hashRepetitions = 1000
generate() }
val generator = PasswordGenerator().apply { seed = "someString" hash = {s -> someHash(s)} hashRepetitions = 1000 } val pasword = generator.generate()
apply
class MyFragment : Fragment(){ companion object { @JvmStatic fun newInstance(b:Boolean): MyFragment { val fragment = MyFragment() val args = Bundle() args.putBoolean("isValid",b) fragment.arguments = args return fragment }
Fragments
Kotlin
fun handleView() { for (item in list) if (item is TextView) item.text = getString(R.string.play) else doRandomOperationOn(item) }
Kotlin
fun handleView() { list.forEach { item -> when (item) { is TextView -> item.text = getString(R.string.play) else -> doRandomOperationOn(item) } }
}
class MyRecyclerAdapter(
val items: List,
val listener: (Item) -> Unit
){
….
}
Kotlin
recycler.adapter = MyRecyclerAdapter(items) { navigateToDetail(it) }
fun View.fadeOut(duration: Long = 500): ViewPropertyAnimator { return animate() .alpha(0.0f) .setDuration(duration) }
Optional params
icon.fadeOut() // fade out with default time (500) icon.fadeOut(1000) // fade out with custom time
Kotlin
inline fun SharedPreferences.edit( func: SharedPreferences.Editor.()-> Unit) { val editor = edit() editor.func() editor.apply() }
//Then preferences.edit { putString("foo", "bar") putString("fizz", "buzz") remove("username") }
CODE
❤ KotlinJava to Kotlin
http://bit.ly/2glBYh8
http://bit.ly/KotlinIsAwesome
Koans - http://bit.ly/KotlinKoans
Thank you
https://medium.com/@aditlal
@aditlalhttp://bit.ly/devfestKotlin