2. What’s Kotlin
● Kotlin is a new programming language targeting JVM,
Android and even JavaScript.
● 100% inter-operable with Java™ (Automatic conversion)
● No NPE (NullPointerException)
● Supports Java 8 features like Lambda expressions,
Streams, Try with resource.
● Ability to add methods to Platform APIs.
3. Why Kotlin is good
● Kotlin compiles to JVM bytecode or JavaScript.
● Kotlin comes from industry, not academia. It solves
problems faced by working programmers today.
● Costs nothing to adopt! It’s open source.
● Kotlin programs can use all existing Java frameworks and
libraries.
● No particular philosophy of programming & runtime
overhead.
● Adopting Kotlin is low risk.
4. Syntax - Variables
● var - something that will vary with time. (Mutable )
● val - for a value that won’t change. (Immutable - i.e. read-only)
val message : String = "Kotlin Magic" // Explicit definition
var messages: List<String> = ArrayList()
● No new keyword
5. Syntax - Functions
● Semicolon free language.
● Officially called single-expression functions
● No extra lines and no braces required.
● Lose the “return” keyword and the return type is
automatically inferred.
fun add(a: Int, b: Int): Int {
return a + b
}
fun add(a: Int, b: Int) = a + b
6. Features - Null-ability
● Catching null(s) during compile time.
● While declaring a variable as null:
● var message: String = null // Compile-time error
● var message: String? = null // Okay
● Compiler shows compile time error if an object is null-
able
message?.let { println(it) }
7. Features - Elvis operator ?:
If something is null, I want to give it a value, but
otherwise just leave it alone.
// Java
if (people == null) {
people = new ArrayList();
}
return people;
// Kotlin
return people ?: emptyArrayList()
8. Features - Mutability
● Compiler comes with Mutability Protection to prevent NPEs
on var type variables.
● Thread-safe & no synchronization issues
● Concurrent programming safer and cleaner.
Mutable object – Can change the states
and fields after the object is created. Ex:
StringBuilder
Immutable object – Cannot change
anything after the object is created. Ex:
String
9. Features - Lambda Expressions
● Higher Order Function - passed as an argument to another
function.
● Beautifying even the ugliest click handlers,
mButton.setOnClickListener {
// Your click logic
}
● Cut to the main logic & code so clean and simple.
10. Feature - Smart Casting
if (view is TextView) {
view.setText("So much redundancy"); // Smart Cast
}
● The Compiler does smart casting on our behalf.
● Handles the negative scenario like if the “view” is not
an “TextView”.
11. Setting up Kotlin in Android Studio
● Open Android Studio Settings (Ctrl + Alt + S)
● Go to Plugins
● Install only the “Kotlin” plugin
● Restart the IDE
Tools > Kotlin > Configure Kotlin in Project
Convert Java to Kotlin file - Just Copy & paste
12. Cool things in Kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
● Inheritance- No “extends” & “implements”,Replaced with a
“:”
● “?” - Allow you to use the object in case it value exists
otherwise it will ignore it.
● val resource = context?.getResources() // Safe
13. If context has value,
// A better approach
if (context != null) {
// Smart cast, Thank you Compiler!
val res = context.getResources()
}
// Another approach
context?.let {
// Execute this block if not null
val res = context.getResources() }
// Yet another approach
context?.apply {
// Execute this block if not null
val res = getResources() }
14. Let & Apply block
Want to define a variable for a
specific scope of your code but
not beyond.
Similar to let block
Used to test null condition Part of the calling type
Useful to keep your code nicely
self-contained so that you don’t
have variables “leaking out”.
i.e. not calling
context.getResources() but
instead we are directly calling
getResources()
Comes with standard library of Kotlin
Let Apply
15. apply vs with
‘apply’ accepts an instance as the receiver while ‘with’ requires an instance
to be passed as an argument.
fun getDeveloper(): Developer {
return Developer().apply {
developerName = "Amit Shekhar"
developerAge = 22
}
}
Usually you use apply when you need to do something with an object and return it. And when
you need to perform some operations on an object and return some other object you can use
with.
fun getPersonFromDeveloper(developer: Developer): Person {
return with(developer) {
Person(developerName, developerAge)
}
}
16. Free Functions
● Functions or variables can be declared at the top level
in a file (*.kt) without any class.
● Default static in nature without any class holding them.
● Can write free functions and access them from another
Kotlin class or file directly (without Class name).
● But, functions come with access specifiers to control the
visibility.
17. Goodbyes to findViewById
● Kotlin (specifically Android Extensions) do the heavy
lifting for you.
apply plugin: 'kotlin-android-extensions' in gradle
● For the layout filename activity_main.xml, we'd need to
import
import kotlinx.android.synthetic.main.activity_main.*
● txtTitle.text = "Hello Kotlin"
18. findViewById - At Background,
● No more view binding.
● Internally the compiler creates a small hidden cache
● Activity or Fragment giving us the synthetic property to
use the view.
● Lazy initialized i.e. when you use the view in the code
● Next time you use the same view it will fetch it from
cache.
19. findViewById - Exceptions
● Android Extension doesn’t work on MenuItem.
● Could be referencing to a layout that is not a direct
children of that one.
● Compiler won’t give you warning or error in this case But
it will fail during the execution time.
● When it tries to get the view that doesn’t exist.
20. lateinit vs lazy (Property initialization feature)
lateinit ( late initialization)
● Properties can be initialized through dependency
injection, or in the setup method of a unit test.
public class Test {
lateinit var mock: Mock
@SetUp fun setup() {
mock = Mock()
}
@Test fun test() {
mock.do()
}
}
21. lateinit ..,
● Cannot supply a non-null initializer in the constructor,
but you still want to avoid null checks.
● To handle this case, you can mark the property with the
lateinit modifier.
● Only be used on var properties declared inside the body
of a class.
● And when the property does not have a custom getter or
setter.
22. lazy function ( lazy initialization)
● Takes a lambda and returns an instance of lazy.
● Serve as a delegate for implementing a lazy property.
● First call to get() executes the lambda passed to lazy()
and remembers the result.
public class Example{
val name: String by lazy { “Steve jobs” }
}
First and subsequent calls, name will return “Steve jobs”
23. When to use which one?
● Lazy is a good fit for properties that may or may not be
accessed.
● If we never access them, we avoid computing their initial
value.
● Good for Activity - Not accessed before setContentView
● Crash in Fragment - View configuration is done in
onViewCreated
24. When to use which one?
● Activities or Fragments - Makes more sense to use
lateinit for their properties, especially the ones
referencing views.
● Don’t control the lifecycle, we know when those
properties will be properly initialized.
● Downside - Ensure they are initialized in the
appropriate lifecycle methods.
25. Data Class
data class Developer(val name: String, val age: Int)
● No need hashCode(), equals(), toString(), and copy()
● Compiler automatically create these internally, so it also leads to clean
code.
Requirements that data classes need to fulfil:
● The primary constructor needs to have at least one parameter.
● All primary constructor parameters need to be marked as val or var
● Data classes cannot be abstract, open, sealed or inner.
26. Destructuring - Returning Two Values from a
Function
Convenient way of extracting multiple values from data.
data class Developer(val name: String, val age: Int)
fun getDeveloper(): Developer {
// some logic
return Developer(name, age)
}
// Now, to use this function:
val (name, age) = getDeveloper()
Destructuring Declarations and Maps
for ((key, value) in map) {
// do something with the key and the value
}
27. sealed classes
● Restricted class hierarchies, also allowing a datatype to be one of a
predefined set of types.
● Combined with when expression & Verify all cases and else unnecessary.
sealed class Car {
data class Maruti(val speed: Int) : Car()
data class Bugatti(val speed: Int, val boost: Int) : Car()
object NotACar : Car()
}
fun speed(car: Car): Int = when (car) {
is Car.Maruti -> car.speed
is Car.Bugatti -> car.speed + car.boost
Car.NotACar -> INVALID_SPEED
// else clause is not required as we've covered all the cases
}
28. Sealed classes have the same behavior as enum
classes
● An extension of enum classes: the set of values for an
enum type is also restricted
● enum - single instance, a subclasses of a sealed class -
multiple instances.
● Key benefit of using sealed classes comes into play when
you use them in a when expression.
● If constant behavior, so your pick is an enum, otherwise,
sealed is your choice.
29. Replacing simple if/else if/else/switch blocks with
when
if (firstName.equals("Dan")) {
person.setTeam(programmers);
} else if (lastName.equals("Dihiansan")) {
person.setTeam(designers);
} else {
person.setTeam(others);
}
switch (firstName) {
case "Dan":
person.setTeam(programmers)
break;
case "Jay":
person.setTeam(programmers)
break;
case "Jamie":
person.setTeam(designers)
break;
default:
person.setTeam(others)
}
In JAVA
31. Convenience methods built on top of familiar
objects
Extended objects you’re familiar with and made them even
better and packaged them into the Kotlin Standard Library.
// Java
if (name.toLowerCase().contains(firstName.toLowerCase())) {
...
}
// Kotlin
if (name.contains(firstName, true)) { ... }
32. Best Practices - do things the Kotlin way
1. Named Arguments instead of Fluent Setter
2. apply() for Grouping Object Initialization
3. Don’t Overload for Default Arguments
4. Leverage Value Objects
5. Refer to Constructor Parameters in Property Initializers
6. Ad-Hoc Creation of Structs
33. 1. Named Arguments instead of Fluent Setter
● Simulate named and default arguments and to make huge
parameter lists
● In Kotlin, named and default arguments fulfil the same
propose but are built directly into the language
//Don't
val config = SearchConfig()
.setRoot("~/folder")
.setTerm("kotlin")
.setRecursive(true)
.setFollowSymlinks(true)
//Do
val config2 = SearchConfig2(
root = "~/folder",
term = "kotlin",
recursive = true,
followSymlinks = true
)
34. 2. apply() for Grouping Object Initialization
● Helps to group and centralize initialization code for an
object.
● apply() is often useful when dealing with Java libraries
in Kotlin.
//Don't
val dataSource = BasicDataSource()
dataSource.driverClassName =
"com.mysql.jdbc.Driver"
dataSource.url =
"jdbc:mysql://domain:3309/db"
dataSource.username = "username"
dataSource.password = "password"
//Do
val dataSource = BasicDataSource().apply {
driverClassName =
"com.mysql.jdbc.Driver"
url = "jdbc:mysql://domain:3309/db"
username = "username"
password = "password" }
35. 3. Don’t Overload for Default Arguments
● Don’t overload methods and constructors to realize
default arguments (so called “method chaining” or
“constructor chaining”).
● In fact, default arguments remove nearly all use cases
for method and constructor overloading
//Don't
fun find(name: String){
find(name, true)
}
fun find(name: String, recursive: Boolean){
}
//Do
fun find(name: String, recursive: Boolean = true){
}
36. 4. Leverage Value Objects
● With data classes, writing immutable value objects is so
easy.
● Even for value objects containing only a single property.
● So there is no excuse for not using value objects
anymore!
//Don't
fun send(target: String){}
//Do
fun send(target: EmailAddress){}
// expressive, readable, type-safe
data class EmailAddress(val value: String)
37. 5. Refer to Constructor Parameters in Property
Initializers
● Define a constructor body (init block) only to initialize
properties.
● apply() can help to group initialization code and get
along with a single expression.
//Don't
class UsersClient(baseUrl: String, appName: String) {
private val httpClient: HttpClient
init {
val builder = HttpClientBuilder.create()
builder.setUserAgent(appName)
builder.setConnectionTimeToLive(10,
TimeUnit.SECONDS)
httpClient = builder.build()
}
}
//Do
class UsersClient(baseUrl: String, appName:
String) {
private val httpClient =
HttpClientBuilder.create().apply {
setUserAgent(appName)
setConnectionTimeToLive(10,
TimeUnit.SECONDS)
}.build()
}
38. 6. Ad-Hoc Creation of Structs
● listOf, mapOf and the infix function to can be used to
create structs (like JSON) quite concisely.
● Use data classes and object mapping to create JSON. But
sometimes (e.g. in tests) this is very useful.
//Do
val customer = mapOf(
"name" to "Clair Grube",
"languages" to listOf("german", "english"),
"address" to mapOf(
"city" to "Leipzig",
)
)