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

Kotlin: A language for productive developers

Kotlin: A language for productive developers

This talk shows how the features in the Kotlin language can be used to make you a more productive developer. The current version corresponds to the one presented on Betabeers Granada in June 2017.

You can also see the sources for the slides at: https://github.com/jrgonzalezg/kotlin-overview-talk

Juan Ramón González

June 22, 2017
Tweet

More Decks by Juan Ramón González

Other Decks in Programming

Transcript

  1. KOTLIN: A LANGUAGE FOR PRODUCTIVE DEVELOPERS Olmo Gallegos Juan Ramón

    González González App&Web GoMore / Amovens (formerly Quadbits) @voghDev @jrgonzalezg
  2. INTRODUCTION The Kotlin language Basic features Extension functions Functional Programming

    (FP) Kotlin on Android Advanced topics Advanced Types Domain Specific Languages (DSLs) Coroutines To know more
  3. THE KOTLIN PROGRAMMING LANGUAGE Expressive Type-safe Functional (we’ll focus on

    that later) Created by JetBrains Official ! First class support in Android since Google I/O 2017 Official support in Spring Framework 5.0 too (final version ~ July 2017)) Made by and for Java “sufferers”
  4. MULTIPLE TARGETS Goal: Make it possible to use Kotlin for

    all parts of any project Kotlin/JVM All features and the whole standard library, production-ready 100% Interoperable with Java and Android JavaScript All language features since Kotlin 1.1 and a large part of the standard library JavaScript interoperability (including Node.js and browser support) Kotlin / Native Machine code through LLVM Mac OS X 10.10 and later (x86-64), x86-64 Ubuntu Linux (14.04, 16.04 and later), Apple iOS (arm64), Raspberry Pi, cross-compiled on Linux host, more to come... Not yet feature complete
  5. BASIC FEATURES Data classes Null-safety Inheritance (open, closed classes) Interfaces

    with attrs, default methods Default values for attributes Smart casts Elvis operator Multiple inheritance Testability (JUnit4 100% supported)
  6. DATA CLASSES Java Kotlin public class Book { int id;

    String title; int coverId; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getCoverId() { return coverId; } public void setCoverId(int coverId) { this.coverId = coverId; } } data class Book(val id: Int, val title: String, val coverId: Int)
  7. NULL-SAFETY Java (all non-primitive types are nullable in an unsafe

    way) This code causes NPE! Book book = null; book.getTitle();
  8. NULL-SAFETY Kotlin - Trying to use null in Non-Null types

    This code does not compile - "null can not be a value of a non-null type Book" val book: Book = null print(book.title)
  9. NULL-SAFETY - NULLABLE TYPES This code does not compile -

    "only safe (?.) or non-null asserted (!!) calls are allowed on a nullable receiver of type Book" One way to make this work would be using the ? operator val book: Book? = null print(book.title)
  10. NULL-SAFETY - NULLABLE TYPES This code works, but prints nothing.

    "title" is never evaluated because "book" is null. NPE is avoided. val book: Book? = null print(book?.title)
  11. NULL-SAFETY - NULLABLE TYPES You can also assert non-null using

    the !! operator This causes NPE, as we are forcing "book.title" to be evaluated, but "book" is null. Would behave similar to Java val book: Book? = null print(book!!.title)
  12. INHERITANCE Given the previous data class "Book" We want to

    create a subclass called "Novel" We find that we can't. Compiler yields "this type is final, so we can't inherit from it" Book needs to be declared as open class for inheritance to work data class Book(val id: Int, val title: String, val coverId: Int) class Novel(id: Int = 0, title: String = "", coverId: Int = 0, var summary: String = "") : Book(id, title, coverId) open class Book(val id: Int, val title: String, val coverId: Int)
  13. INTERFACES WITH ATTRIBUTES AND DEFAULT METHODS In Kotlin, interfaces can

    have attributes Java Kotlin public interface Request<T> { T client; // Does not compile void execute(); } interface Request<T> { val client : T fun execute() }
  14. INTERFACES WITH ATTRIBUTES AND DEFAULT METHODS Java: Default template methods

    are supported in abstract classes Kotlin public abstract class Request<T> { T client; // Compiles void execute() { // Default implementation for method } } interface Request<T> { val client : T fun execute() { // Default implementation } }
  15. DEFAULT VALUES FOR ATTRIBUTES Java: Telescoping constructor problem - Builder

    pattern Kotlin: Attributes with default values to the rescue! data class Book(val id: Int = 0, val title: String = "", val coverId: Int = 0) fun initBooks() { val books = listOf( Book(id = 1), Book(id = 2, title = "Moby dick"), Book(id = 3, title = "Sherlock Holmes", coverId = 254), Book() ) books.forEach { print(it.title) } }
  16. SMART CASTS Null safety Adding a null check does the

    smart cast val book : Book? book.title // Does not compile book?.title // Ok if (book != null) { book.title // Ok }
  17. SMART CASTS Smart type casts open class Book(val id: Int

    = 0, val title: String = "", val coverId: Int = 0) class Novel(id: Int = 0, title: String = "", coverId: Int = 0, var summary: String = "") : Book(id, title, coverId) val novel: Book = Novel() (novel as Novel).summary = "Once upon a time..." if (novel is Novel) { novel.summary = "Once upon a time..." }
  18. ELVIS OPERATOR Java Kotlin String title; System.out.println(title != null ?

    title : "-"); List<Novel> novels; System.out.println( novels != null && novels.size() > 0 ? String.format("%d novels", novels.size()) : "No novels" ); val title: String? print(title ?: "-") val novels : List<Novel>? print("${novels?.size ?: "No"} novels")
  19. MULTIPLE INHERITANCE A car can be a mean of Transportation,

    and also a Piece of museum interface Transportation { fun move() fun emitCO2() } interface PieceOfMuseum { fun shine() fun lookGreat() } class Car : Transportation, PieceOfMuseum { override fun move() { } override fun shine() { } override fun emitCO2() { } override fun lookGreat() { } }
  20. MULTIPLE INHERITANCE BY DELEGATION Car class is built with two

    parameters, one is responsible for the "Transportation" behavior, and the other one for the "PieceOfMuseum" class LuxuryRacingCar(t: Transportation, p: PieceOfMuseum) : Transportation by t, PieceOfMuseum by p { fun letTheShowBegin() { move() shine() emitCO2() lookGreat() } }
  21. TESTABILITY JUnit is 100% supported Test names are very readable,

    near specification of real requirements @Test fun 'should request a List of users on start'() { val presenter = UserListPresenter(mockContext, userRepository) presenter.view = mockView presenter.initialize() verify(userRepository, times(1))?.getUsers() }
  22. TESTABILITY: MOCKITO SUPPORT Similarly to Java, Mockito is supported A

    Small issue related to final classes (easy to solve) adds some syntactic sugar mockito-kotlin @Test fun 'should show a list of users if previous request has results'() { val presenter = UserListPresenter(mockContext, userRepository) `when`(userRepository.getUsers()).thenReturn(listOf(User(name = "John"))) presenter.view = mockView presenter.initialize() verify(mockView, times(1))?.showUsers(anyList()) }
  23. TESTABILITY: MOCKITO SUPPORT Typical Mockito annotations and setUp method -

    similar to Java Test names can be made even more readable using third-party components like @Mock lateinit var userRepository: UserRepository @Mock lateinit var mockContext: Context @Mock lateinit var mockView: SomeDetailPresenter.MVPView @Before fun setUp() { MockitoAnnotations.initMocks(this) } kotlintest
  24. EXTENSION FUNCTIONS Ability to extend a class with new functionality

    without inheriting from it No need to use patterns like Decorator Extension functions do not modify the receiver classes Excellent substitute to Helper methods and Util classes
  25. EXTENSION FUNCTIONS Declared with class name as prefix, then function

    name fun Activity.screenWidth(): Int { val metrics: DisplayMetrics = DisplayMetrics() windowManager.defaultDisplay.getMetrics(metrics) return metrics.widthPixels } fun Activity.screenHeight(): Int { val metrics: DisplayMetrics = DisplayMetrics() windowManager.defaultDisplay.getMetrics(metrics) return metrics.heightPixels }
  26. EXTENSION FUNCTIONS Then the extension function can be used inside

    Activity class class SomeActivity : AppCompatActivity() { // ... fun loadImage(url: String) { Picasso.with(this) .load(url) .resize(screenWidth(), 200) .into(imageView) } }
  27. SOME PRINCIPLES A computation can be viewed as a function

    that maps inputs to outputs Pure functions Always return the same result for the same arguments The evaluation of the result has no observable side effects Function signature does not lie You can infer what the function may do by its signature If the function has no parameters it should return a constant
  28. SOME PRINCIPLES Immutability by default Immutable data can not be

    changed after it is created State changes are represented by new instances of data Original instances are not affected Simplifies multi-threaded applications (race conditions) data class Book(val id: Int, val title: String, val coverId: Int) val book = Book(id = 3, title = "Sherlock Holmes", coverId = 254) // Immutable val modifiedBook = book.copy(coverId = 2) // Copy is a separate immutable instance
  29. FIRST-CLASS FUNCTIONS Functions as a first-class citizen of the language

    Higher-order functions Functions can return functions Functions can be passed as arguments to other functions Functions can be assigned to variables and stored inside other data structures Lambda Expressions Functions that are not declared but passed immediately as an expression
  30. LAMBDA EXPRESSIONS Always surrounded by curly braces map on List

    is a higher-order function You can pass a lambda expression as the transform function fun <T, R> List<T>.map(transform: (T) -> R): List<R> { val result = arrayListOf<R>() for (item in this) result.add(transform(item)) return result } val ints = listOf(1, 2, 3) val doubled = ints.map { value -> value * 2 } // [2, 4, 6] println(ints.map { it * 3 } ) // [3, 6, 9] - Implicit name for single parameter
  31. WITH, LET, APPLY Java Kotlin final TextView textView = new

    TextView(this); textView.setText("Hello!"); textView.setTextSize(16f); textView.setTextColor(ContextCompat.getColor(this, android.R.color.white)); val textView : TextView = TextView(this) with(textView) { text = "" textSize = 16f setTextColor(getColor(android.R.color.white)) } // with is a higher-order function returning R // block is a lambda with receiver (an extension lambda) returning R public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
  32. WITH, LET, APPLY Java Kotlin LinearLayout linearLayout = new LinearLayout(this);

    if (textView != null) { linearLayout.addView(textView); setContentView(linearLayout); } val linearLayout = LinearLayout(this) textView?.let { linearLayout.addView(it) setContentView(linearLayout) } // let is a higher-order extension function returning R // block is a function from (T) to R public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
  33. WITH, LET, APPLY Java Kotlin TextView textView = new TextView(this);

    textView.setText("Hello!"); textView.setTextSize(16f); textView.setTextColor(ContextCompat.getColor(this, android.R.color.white)); val textView: TextView = TextView(this).apply { text = "Hello!" textSize = 16f setTextColor(getColor(android.R.color.white)) } // apply is a higher-order extension function returning Unit // block is a lambda with receiver (an extension lambda) returning Unit public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
  34. COLLECTIONS Kotlin distinguishes mutable and immutable collections You have control

    over when collections can be edited Helps to eliminate bugs and to design better APIs Currently the immutable collections are really read-only versions of mutable collections Prototypes of real immutable collections with the performance characteristics of typical FP persistent data structures val ids: List<Int> = listOf(1, 2, 3) println(ids.get(2)) // 3 ids.add(0, 6) // Unresolved reference: add val numbers: MutableList<Int> = mutableListOf(5, 9, 3) numbers.add(0, 6) println(numbers) // [6, 5, 9, 3] Immutable Collections Library for Kotlin
  35. FUNCTIONAL COMBINATORS John Hughes: A combinator is a function which

    builds programs fragments from program fragments Functional Programming languages usually provide a big set of combinators for collections val strings = listOf("Kotlin", "is", "here", "to", "stay") val result = strings.filter { it.length == 4 } .map(String::toUpperCase) // Method reference .sortedByDescending { it } // Natural sort order .joinToString(separator = " ") println(result) // STAY HERE
  36. FUNCTIONAL COMBINATORS val strings = listOf("Kotlin", "is", "here", "to", "stay")

    val result: Map<Char, Int> = strings .filterNot { it.contains('t') } // Keep only strings without 't' .flatMap { it.toList() } // A list of chars from the list of strings .groupBy { it } // A map from each char to its occurrences .mapValues { it.value.size } // A map from char to occurrence count .toList() // List of pairs (char, count) .sortedBy { it.second } // Sorted by increasing count println(result) // [(i, 1), (s, 1), (h, 1), (r, 1), (e, 2)]
  37. KOTLIN ON ANDROID : Pleasant Android application development Huge set

    of tools to clean and simplify your Android code Anko Layouts: View binding similar to @BindView Pleasant SQLite management. No need to use an ORM async and doAsync to manage asynchronous calls Also supports Coroutines (described later) anko ButterKnife's
  38. VIEW BINDING EXAMPLE This widget can be directly accessed using

    anko No need to use findViewById or similar <Button android:id="@+id/btn_start" android:layout_width="180dp" android:layout_height="180dp" android:text="@string/start" android:textColor="@color/white" android:textSize="20sp" /> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) btn_start.setOnClickListener { // Manage button tap here } } override fun hideStartButton() { btn_start.visibility = INVISIBLE }
  39. TYPE HIERARCHY Any Any? is the root of the hierarchy

    (a supertype of any other type) Any is a subtype of Any? and the root of the Non-Null types Unit A type with only one value: the Unit object Corresponds to the void type in Java
  40. TYPE HIERARCHY Nothing Nothing has no instances Represents a value

    that never exists Example: Functions that never return (always throw an exception) Bottom type: It is a subtype of any other type public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason") fun doSomething: A = TODO("This should do something!") // Valid implementation
  41. TYPE HIERARCHY Good general explanation of Kotlin Types Relation between

    Nullable and Non-Null Types Nat Pryce - Mistaeks I Hav Made: A Whirlwind Tour of the Kotlin Type Hierarchy
  42. VARIANCE Definition: A <: B if A is a subtype

    of B What if we have generics (type constructors) like List? Covariant: interface List<out A> If A <: B then List<A> <: List<B> Contravariant: interface List<in A> If A <: B then List<A> >: List<B> Invariant: interface List<A> Neither List<A> nor List<B> are a subtype of the other
  43. VARIANCE List in Kotlin is covariant - Why? It is

    read-only so A never appears in a contravariant position MutableList is invariant You can control variance and the compiler enforces it Use @UnsafeVariance annotation if you know something is safe despite it appears in an unsafe position interface List<out A> { fun get(index: Int): A // Safe fun add(a: A): Boolean // Unsafe } var cats: List<Cat> = listOf(cat1, cat2, cat3) var animals: List<Animal> = cats val animal = animals.get(0) // Ok! animals.add(dog1) // Problem - Adding a dog to a list of cats!
  44. VARIANCE // File: kotlin.jvm.functions.Functions.kt /** A function that takes 1

    argument. */ public interface Function1<in P1, out R> : Function<R> { /** Invokes the function with the specified argument. */ public operator fun invoke(p1: P1): R } // Int <: Number, Novel <: Book, so Function1<Number, Novel> <: Function1<Int, Book> val queryNovelFunc: Function1<Number, Novel> = { Novel() } val queryBookFunc: Function1<Int, Book> = queryNovelFunc val book: Book = queryBookFunc(1) val book2: Book = queryNovelFunc(7.0)
  45. ALGEBRAIC DATA TYPES (ADTS) - PRODUCT TYPES Seen in the

    wild as: Tuples, Records, POJOs… Directly supported in most languages Kotlin: Ideally as a data class Always contains an id, a title and a coverId There are as many possible inhabitants of the type as the product of the number of values in each of the composing subtypes data class Book(val id: Int, val title: String, val coverId: Int)
  46. ALGEBRAIC DATA TYPES (ADTS) - SUM TYPES Also named: Coproducts,

    Tagged Types, Disjoint Unions or Variant Types No direct support on many languages Kotlin: Use sealed classes It can contain only one of the enclosed types and nothing else As many possible inhabitants as the sum of inhabitants on each subtype sealed class BookResult { class BookFound(val book: Book) : BookResult() object BookNotFound : BookResult() object BookUnavailable : BookResult() }
  47. ALGEBRAIC DATA TYPES (ADTS) - EXAMPLE Based on Domain Driven

    Design | F# for fun and profit typealias ContactName = String sealed class ContactInfo { class EmailContactInfo(val email: String): ContactInfo() class PostalContactInfo(val address: String): ContactInfo() } // A Contact can not be created without a name and at least one ContactInfo data class Contact(val name: ContactName, val primaryContactInfo: ContactInfo, val secondaryContactInfo: ContactInfo? = null) fun sendMessageUsingPrimaryContactInfo(contact: Contact): Unit = when (contact.primaryContactInfo) { // when branches are autocompleted, perform smarcasts and the // compiler ensures you handle all ContactInfo options => Less tests is ContactInfo.EmailContactInfo -> sendEmail(contact.primaryContactInfo) is ContactInfo.PostalContactInfo -> TODO() // TODO() should not be in production code - Use detekt, checkstyle, findbugs } abstract fun sendEmail(emailContactInfo: EmailContactInfo) // Somewhere
  48. HIGHER-KINDED TYPES (HKTS) Generics only allow to abstract over the

    enclosed type What if we also want to abstract over the type constructor itself? It is allowed on languages like Haskell or Scala (F[_]) Kotlin does not support this directly kategory!
  49. KATEGORY: FUNCTIONAL DATA TYPES AND ABSTRACTIONS FOR KOTLIN Based on

    Custom encoding to allow defining HKTs and type classes Inspired by With Free monads! Totally separate program definition from program interpretation Cats highj https://github.com/kategory/kategory
  50. DOMAIN SPECIFIC LANGUAGES (DSLS) : Kotlin language support for Gradle

    build scripts Covers yet another part of a whole project Gradle Kotlin DSL Life is Great and Everything Will Be Ok, Kotlin is Here (Google I/O '17) val db: SQLiteDatabase = // ... db.transaction { delete("users", "first_name = ?", arrayOf("jake")) // …. } inline fun SQLiteDatabase.transaction(body: SQLiteDatabase.() -> Unit) { beginTransaction() try { body() setTransactionSuccessful() } finally { endTransaction() } }
  51. COROUTINES Computations that can be suspended without blocking a thread

    Very lightweight: Millions of coroutines can run on a few threads A suspended coroutine does not consume a thread Allow to express an asynchronous computation on a sequential way No need to use callbacks Experimental feature on Kotlin 1.1 (API may still change) Implemented through compiler transformations, no OS / VM support needed Language support: Suspending functions (suspend keyword) Low level core API in the Kotlin Standard Library High level APIs that can be used directly in the user code
  52. COROUTINES Guide to kotlinx.coroutines by example suspend fun doSomethingUsefulOne(): Int

    { delay(1000L) // pretend we are doing something useful here return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) // pretend we are doing something useful here, too return 29 }
  53. COROUTINES It produces something like this: The answer is 42

    Completed in 1017 ms fun main(args: Array<String>) = runBlocking<Unit> { val time = measureTimeMillis { val one = async(CommonPool) { doSomethingUsefulOne() } val two = async(CommonPool) { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") }
  54. TO KNOW MORE References Books Hadi Hariri - VP of

    Developer Advocacy, JetBrains - Kotlin talk Documentation - Kotlin Programming Language Kotlin and Android | Android Developers Kotlin for Android Developers - Antonio Leiva Kotlin in Action - Dmitry Jemerov and Svetlana Isakova Lambda World - October 26th & 27th, 2017
  55. TO KNOW MORE Some learning and feature testing with the

    Kotlin language Model View Presenter (MVP) Repository Pattern Mockito, synchronous and asynchronous unit tests to cover all basic cases Package by layer (Incomplete WIP) Sample Android App to query and display Open Library data Dependency Injection (DI) with Dagger 2 Clean Architecture, Repository Pattern, MVP Domain-driven design, Package by feature Functional Programming (FP), Coroutines HelloKotlin OpenLibraryApp
  56. THANKS FOR COMING! Olmo Gallegos Juan Ramón González González App&Web

    GoMore / Amovens (formerly Quadbits) @voghDev @jrgonzalezg https://github.com/jrgonzalezg/kotlin-overview-talk