美文网首页
31天Kotlin

31天Kotlin

作者: Showdy | 来源:发表于2018-11-23 15:16 被阅读83次

    转载:31DaysOfKotlin

    # 31DaysOfKotlin — Week 1 Recap

    The moreKotlin code we write, the more we love it!Kotlin’s modern language features together with Android KTX made our Android code more concise, clear and pleasant. We (@FMuntenescu and @objcode) started the #31DaysOfKotlin series as a way of sharing some of our favorite KotlinandAndroid KTXfeatures and hopefully get more of you to like it as much as we do.


    ![week2of4.png](https://img.haomeiwen.com/i1742298/e876542e2f186a03.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    In the first 7 days of Kotlin we focused on the basics.

    Day 1: Elvis operator

    Handling nulls in style? Check out the elvis operator ?:, to cut your “null-erplate”. It’s just a small bit of syntax sugar to replace nulls with a default value or even return! Docs: Elvis operator.

    val name: String = person.name ?: “unknown”
    val age = person.age ?: return
    

    Day 2: String templates

    Formatting Strings? Refer to variables and expressions in string literals by putting $ in front of the variable name. Evaluate expressions using ${expression}. Docs: string templates.

    val language = "Kotlin"
    // “Kotlin has 6 characters”
    val text = "$language has ${language.length} characters"
    

    Day 3: Destructuring declarations

    Now with prisms? Android KTX uses destructuring to assign the component values of a color. You can use destructuring in your classes, or extend existing classes to add destructuring. Docs: destructuring declarations.

    // now with prisms
    val (red, green, blue) = color
    // destructuring for squares
    val (left, top, right, bottom) = rect
    // or more pointedly
    val (x, y) = point
    

    Day 4: When expressions

    A switch statement with superpowers? Kotlin’s when expression can match on just about anything. Literal values, enums, ranges of numbers. You can even call arbitrary functions! Docs: when

    class Train(val cargo: Number?) {
        override fun toString(): String {
            return when (cargo) {
                null, 0 -> "empty"
                1 -> "tiny"
                in 2..10 -> "small"
                is Int -> "big inty"
                else -> "$cargo"
            }
        }
    }
    

    Day 5: For loops, range expressions and destructuring

    For loops get superpowers when used with two other Kotlin features: range expressions and destructuring. Docs: ranges, destructuring.

    // iterating in the range 1 to 100
    for(i in 1..100) {…}
    // iterating backwards, in the range 100 to 1
    for(i in 100 downTo 1){…}
    // iterating over an array, getting every other element
    val array = arrayOf(“a”, “b”, “x”)
    for(i in 1 until array.size step 2 ){…}
    // iterating over an array with the item index and destructuring
    for((index, element) in array.withIndex()) {…}
    // iterating over a map
    val map = mapOf(1 to “one”, 2 to “two”)
    for( (key, value) in map){…}
    

    Day 6: Properties

    In Kotlin, classes can have mutable and read-only properties, with getters and setters generated by default. You can also implement custom ones if required. Docs: properties.

    class User {
        // properties
        val id: String = "" // immutable. just getter
        var name: String = "" // default getter and setter
        var surname: String = "" // custom getter, default setter
        get() = surname.toUpperCase() // custom getter declaration
        var email: String = "" // default getter, custom setter
        set(value) { // custom setter declaration
        // “value” = name of the setter parameter
        // “field” = property’s backing field; generated
            if(isEmailValid(value)) field = value
        }
    }
    

    Day 7: Data classes and equality

    Creating classes with one role: to hold data? Mark them as “data” classes. The default implementation of equals() is generated (so are hashCode(), toString(), and copy()) and checks for structural equality. Docs: data classes, equality

    data class User(
        val name: String, 
        val email: String, 
        val address: Address, 
        … 
    )
    public class UserListDiffCallback: DiffUtil.Callback() {
        override fun areContentsTheSame(
             oldItemPosition: Int,  
             newItemPosition: Int
        ): Boolean { 
        // use the generated equals method
        return newUserList[newItemPosition] == 
               oldUserList[oldItemPosition])
    }
    

    This week focused on the basics: removing null errors, simplifying loops and conditions, improving getters and setters, and removing boilerplate. Next week we’ll dive into more Kotlin features!


    ![week3of4.png](https://img.haomeiwen.com/i1742298/8426d35ca6659b15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    In the second we we continued to explore Kotlin — going deeper into topics like sealed classes and inline.

    Day 8: Visibility

    In Kotlin, everything is public by default! Well, almost. Kotlin has a rich set of visibility modifiers you can use as well: private, protected, internal. Each of them reduces the visibility in a different way. Docs: visibility modifiers

    // public by default
    val isVisible = true
    // only in the same file
    private val isHidden = true
    // internal to compilation ‘module’
    internal val almostVisible = true
    class Foo {
      // public by default
      val isVisible = true
      // visible to my subclasses
      protected val isInheritable = true
      // only in the same class
      private val isHidden = true
    }
    

    Day 9: Default arguments

    Is the number of method overloads getting out of hand? Specify default parameter values in functions. Make the code even more readable with named parameters. Docs: default arguments

    // parameters with default values
    class BulletPointSpan(
      private val bulletRadius: Float = DEFAULT_BULLET_RADIUS,
      private val gapWidth: Int = DEFAULT_GAP_WIDTH,
      private val color: Int = Color.BLACK
    ) {…}
    // using only default values
    val bulletPointSpan = BulletPointSpan()
    // passing a value for the first argument, others default
    val bulletPointSpan2 = BulletPointSpan(
        resources.getDimension(R.dimen.radius))
    // using a named parameter for the last argument, others default
    val bulletPointSpan3 = BulletPointSpan(color = Color.RED)
    

    Day 10: Sealed classes

    Kotlin sealed classes let you easily handle error data. When combined with LiveData you can use one LiveData to represent both the success path and the error path. Way better than using two variables. Docs: sealed classes

    sealed class NetworkResult
    data class Success(val result: String): NetworkResult()
    data class Failure(val error: Error): NetworkResult()
    // one observer for success and failure
    viewModel.data.observe(this, Observer<NetworkResult> { data ->
      data ?: return@Observer // skip nulls
      when(data) {
        is Success -> showResult(data.result) // smart cast to Success
        is Failure -> showError(data.error) // smart cast to Failure
      }
    })
    

    You can also use sealed classes in a RecyclerView adapter. They’re a perfect fit for ViewHolders— with a clean set of types to dispatch explicitly to each holder. Used as an expression, the compiler will error if all types aren’t matched.

    // use Sealed classes as ViewHolders in a RecyclerViewAdapter
    override fun onBindViewHolder(
      holder: SealedAdapterViewHolder?, position: Int) {
      when (holder) { // compiler enforces handling all types
        is HeaderHolder -> {
          holder.displayHeader(items[position]) // smart cast here
        }
        is DetailsHolder -> {
          holder.displayDetails(items[position]) // smart cast here
        }
      }
    }
    

    Going further with RecyclerViews, if we have a lot of callbacks from a RecyclerView item, like this one with detail clicks, shares, and delete actions, we can use sealed classes. One callback taking one sealed class can handle all the things!

    sealed class DetailItemClickEvent
    data class DetailBodyClick(val section: Int): DetailItemClickEvent()
    data class ShareClick(val platform: String): DetailItemClickEvent()
    data class DeleteClick(val confirmed: Boolean):
         DetailItemClickEvent()
    class MyHandler : DetailItemClickInterface {
      override fun onDetailClicked(item: DetailItemClickEvent) {
        when (item) { // compiler enforces handling all types
          is DetailBodyClick -> expandBody(item.section)
          is ShareClick -> shareOn(item.platform)
          is DeleteClick -> {
            if (item.confirmed) doDelete() else confirmDetele()
          }
        }
      }
    }
    

    Day 11: Lazy

    It’s good to be lazy! Defer the cost of expensive property initialization until they’re actually needed, by using lazy. The computed value is then saved and used for any future calls. Docs: lazy

    val preference: String by lazy {
      sharedPreferences.getString(PREFERENCE_KEY) 
    }
    

    Day 12: Lateinit

    In Android, onCreate or other callbacks initialize objects. In Kotlin non-null vals must be initialized. What to do? Enter lateinit. It’s a promise: initialize me later! Use it to pinky-swear it will eventually be null safe. Docs: lateinit

    class MyActivity : AppCompatActivity() {
      // non-null, but not initalized
      lateinit var recyclerView: RecyclerView
    
      override fun onCreate(savedInstanceState: Bundle?) {
        // …
        // initialized here
        recyclerView = findViewById(R.id.recycler_view)
      }
    }
    

    Day 13: Require and check

    Are your function arguments valid? Check before using them, with require. If they’re not valid an IllegalArgumentException is thrown. Docs: require

    fun setName(name: String) {
      // calling setName(“”) throws IllegalArgumentException
      require(name.isNotEmpty()) { “Invalid name” }
      
      // …
    }
    

    Is the state of your enclosing class correct? Use check to verify. It will throw an IllegalStateException if the value checked is false. Docs: check

    fun User.logOut(){
      // When not authenticated, throws IllegalStateException
      check(isAuthenticated()) { “User $email is not authenticated” }
      isAuthenticated = false
    }
    

    Day 14: Inline

    Can’t wait to use lambdas to make new APIs? Get in line. Kotlin lets you specify a function as inline — which means calls will be replaced with the function body. Breathe and make lambda-based APIs with zero overhead. Docs: inline functions

    // define an inline function that takes a function argument
    inline fun onlyIf(check: Boolean, operation: () -> Unit) {
      if (check) {
        operation()
      }
    }
    // call it like this
    onlyIf(shouldPrint) { // call: pass operation as a lambda
      println(“Hello, Kotlin”)
    }
    // which will be inlined to this
    if (shouldPrint) { // execution: no need to create lambda
      println(“Hello, Kotlin”)
    }
    

    This week went deeper into Kotlin features: visibility, default arguments, sealed classes, lazy, lateinit, require and check, and the really powerful inline. Next week we’ll dive into more Kotlin features and start exploring Android KTX.


    week3of4.png

    Week 3 of Kotlin was split between Kotlin functionalities and different ways of making your Android code sweeter with Android KTX.

    Day 15: Operator overloading

    Write Kotlin (time * 2) faster with operator overloading. Objects like Path, Range or SpannableStrings naturally allow for operations like addition or subtraction. With Kotlin, you can implement your own operators. Docs: operator overloading, Android KTX usage example.

    // Definition
    /** Adds a span to the entire text. */
    inline operator fun Spannable.plusAssign(span: Any) =
    setSpan(span, 0, length, SPAN_INCLUSIVE_EXCLUSIVE)
    // Use it like this
    val spannable = “Eureka!!!!”.toSpannable()
    spannable += StyleSpan(BOLD) // Make the text bold with +=
    spannable += UnderlineSpan() // Make the text underline with +=
    

    Day 16: Top level functions and parameters

    Utility methods for a class? Add them to the top level of the source file. In Java, they are compiled as static methods of that class. Docs: basic syntax.

    // Define a top-level function that creates a DataBinding Adapter for a RecyclerView
    @BindingAdapter(“userItems”)
    fun userItems(recyclerView: RecyclerView, list: List<User>?){
        //update the RecyclerView with the new list
        …
    }
    class UsersFragment: Fragment{...}
    

    Are you defining static constants for your class? Make them top-level properties. They will be compiled to a field and static accessor(s).

    // Define a top-level property for Room database
    private const val DATABASE_NAME = “MyDatabase.db”
    private fun makeDatabase(context: Context): MyDatabase {
        return Room.databaseBuilder(
                       context,
                       MyDatabase::class.java,
                       DATABASE_NAME
                  ).build()
    }
    

    Day 17: Iterating types without an iterator

    Iterators in interesting places? Android KTX adds iterators to ViewGroup and SparseArray. To define iterator extensions use the operator keyword. Foreach loops will use the extensions! Docs: for loops, Android KTX usage example.

    // Example from Android KTX
    for(view in viewGroup) { }
    for(key in sparseArray) {}
    // Your project
    operator Waterfall.iterator() {
       // add an iterator to a waterfall class
    }
    for(items in myClass) {} // Now waterfall has iterations!
    

    Day 18: Easy Content Values

    Combine the power of ContentValues with the brevity of Kotlin. Use the Android KTX ContentValues creator and just pass a Pair<StringKey, Value>. Android KTX implementation.

    val contentValues = contentValuesOf(
        “KEY_INT” to 1,
        “KEY_LONG” to 2L,
        “KEY_BOOLEAN” to true,
        “KEY_NULL” to null
    )
    

    Day 19: DSLs

    Specifically terrific? Domain specific languages can be made by using type safe builders. They make for clean APIs; and you can build them yourself too with the help of features like extension lambdas and type safe builders.

    html {
        head {
                 title {+”This is Kotlin!” }
             }
        body {
                 h1 {+”A DSL in Kotlin!”}
                 p {+”It’s rather”
                    b {+”bold.” }
                    +”don’t you think?”
                    }
              }
    }
    

    Spek is a testing library built as a Kotlin DSL. Instead of using @Annotations, Spek provides a typesafe way to declare your test code without relying on reflection magic.

    @RunWith(JUnitPlatform::class)
    class MyTest : Spek({
        val subject = Subject()
        given("it ’ s on fire") {
            subject.lightAFire()
            it("should be burning") {
                assertTrue(subject.isBurning())
            }
            it("should not be cold") {
                assertFalse(subject.isCold())
            }
        }
    })
    

    Another DSL for Kotlin on Android is Anko. Anko lets you build Android views using declarative code.

    frameLayout {
        button("Light a fire") {
        onClick {
            lightAFire()
        }
    }
    

    Day 20: Easy Bundle

    Bundle up, and get ready for the concise bundle creator in Android KTX. No more calls to putString, putInt, or any of their 20 friends. One call will make you a new Bundle, and it’ll even handle Arrays!

    val bundle = bundleOf(
            "KEY_INT " to 1,
            "KEY_LONG" to 2L,
            "KEY_BOOLEAN" to true,
            "KEY_NULL" to null
            "KEY_ARRAY" to arrayOf(1, 2)
    )
    

    Day 21: Cleaning up postDelayed

    Lambdas are sweet. With last parameter call syntax, you can cleanup callbacks, Callable, and Runnable. For example, Android KTX sweetens postDelayed with a small wrapper.

    // Android KTX API
    fun Handler.postDelayed(
                  delay: Int, 
                  token: Any? = null,  
                  action: () -> Unit)
    // Call it like this — no need for a Runnable in your code
    handler.postDelayed(50) {
             // pass a lambda to postDelayed
    }
    

    This week focused on a few basic Kotlin features like operators overloading, top level functions and parameters and iterators, we talked about an advanced feature: domain specific languages (DSLs) and showed how you can write more concise code when using Android KTX for content values, bundles and callbacks.


    week4of4.png

    Week 4 looked at more language basics then dove into some ways Android KTX makes your code more concise and readable!

    Day 22: Calling Kotlin from the Java Programming Language

    Using Kotlin and Java in the same project? Do you have classes with top-level functions or properties? By default, the compiler generates the class name as YourFileKt. Change it by annotating the file with @file:JvmName. Docs: package level functions

    // File: ShapesGenerator.kt
    package com.shapes
    fun generateSquare(): Square {…}
    fun generateTriangle(): Triangle {…}
    // Java usage:
    Square square = ShapesGeneratorKt.generateSquare();
    // File: ShapesGenerator.kt
    @file:JvmName(“ShapesGenerator”)
    package com.shapes
    fun generateSquare(): Square {…}
    fun generateTriangle(): Triangle {…}
    // Java usage:
    Square square = ShapesGenerator.generateSquare();
    

    Day 23: Reified

    To make the concept of reified concrete an example is in order: Context.systemService() in Android KTX uses reified to pass a “real” type via generics. No more passing classes to getSystemService! Docs: reified type parameters

    Android KTX: Context.systemService()

    // the old way
    val alarmManager =
            context.getSystemService(AlarmManager::class.java)
    // the reified way
    val alarmManager: AlarmManager = context.systemService()
    // the magic from Android KTX… with “real” type T
    inline fun <reified T> Context.systemService() =
             getSystemService(T::class.java)
    

    Day 24: Delegates

    Delegate your work to another class with by. Favor composition over inheritance with class delegation and reuse property accessor logic with delegator properties. Docs: delegation and delegated properties

    class MyAnimatingView : View( /* … */ ) {
        // delegated property. 
        // Uses the getter and setter defined in InvalidateDelegate
        var foregroundX by InvalidateDelegate(0f)
    }
    // A View Delegate which invalidates
    // View.postInvalidateOnAnimation when set.
    class InvalidateDelegate<T : Any>(var value: T) {
        operator fun getValue(thisRef: View, 
                              property: KProperty<*>) = value
        operator fun setValue(thisRef: View, 
                              property: KProperty<*>, value: T) {
            this.value = value
            thisRef.postInvalidateOnAnimation()
        }
    }
    

    Day 25: Extension functions

    No more Util classes! Extend the functionality of a class by using extension functions. Put the name of the class you’re extending before the name of the method you’re adding. Doc: extension functions

    Extension functions:

    • Are not member functions
    • Do not modify the original class in any way
    • Are resolved an compile time by static type information
    • Are compiled to static functions
    • Don’t do polymorphism

    Example: String.toUri()

    // Extend String with toUri
    inline fun String.toUri(): Uri = Uri.parse(this)
    // And call it on any String!
    val myUri = “www.developer.android.com".toUri()
    

    Day 26: Drawable.toBitmap() easy conversions

    If you ever converted a Drawable to a Bitmap then you know how much boilerplate you need.

    Android KTX has a great set of functions to make your code more concise when working with classes from the graphics package. Docs: [graphics](https://github.com/android/android-ktx/tree/master/src/main/java/androidx/core/graphics

        // get a drawable from resources
        val myDrawable = ContextCompat.getDrawable(context, R.drawable.icon)
        // convert the drawable to a bitmap
        val bitmap = myDrawable.toBitmap()
    

    Day 27: Sequences, lazy, and generators

    Sequences are lists that never existed. A Sequence is a cousin of Iterator, lazily generating one value at a time. This really matters when using map and filter — they’ll create Sequences instead of copying the list for every step! Docs: sequences

    val sequence = List(50) { it * 5 }.asSequence()
    sequence.map { it * 2 }         // lazy (iterate 1 element)
            .filter { it % 3 == 0 } // lazy (iterate 1 element)
            .map { it + 1 }         // lazy (iterate 1 element)
            .toList()               // eager (make a new list)
    

    You can make sequences from a list or by specifying a next function. If you never terminate a sequence, it can be infinitely long without running out of memory. With coroutines in Kotlin you can also use generators! Docs: generators

        // make a sequence from a list
        list.asSequence().filter { it == “lazy” }
        
    
        // or, reach infinity functionally
        val natural = generateSequence(1) { it + 1 }
        
    
        // or, cooperate with coroutines
        val zeros = buildSequence {
            while (true) {
                yield (0)
            }
        }
    

    Day 28: Easier spans

    Powerful but hard to use — that’s how the text styling Spans API feels.

    Android KTX adds extension functions for some of the most common spans and makes the API easier to use. Android KTX: spannable string builder

    val string = buildSpannedString {
        append(“no styling text”)
        bold {
            append(“bold”)
            italic { append(“bold and italic”) }
        }
        inSpans(RelativeSizeSpan(2f), QuoteSpan()){
            append(“double sized quote text”)
        }
    }
    

    Day 29: Parcelize

    Love the speed of Parcelable, but don’t like writing all that code? Say hello to @Parcelize. Spec: Parcelize

    @Parcelize
    data class User(val name: String, val occupation: Work): Parcelable
    // build.gradle
    androidExtensions {
        //Enable experimental Kotlin features 
        // in gradle to enable Parcelize
        experimental = true
    }
    

    Day 30: updatePadding extensions

    Extending existing APIs with default arguments usually makes everyone happy. Android KTX lets you set the padding on one side of a view using default parameters. A one line function that saves so much code! Android KTX: View.updatePadding

    view.updatePadding(left = newPadding)
    view.updatePadding(top = newPadding)
    view.updatePadding(right = newPadding)
    view.updatePadding(bottom = newPadding)
    view.updatePadding(top = newPadding, bottom = newPadding)
    

    Day 31: Scoping out run, let, with, apply

    Let’s run with some standard Kotlin functions! Short and powerful, run, let, with, and apply all have a receiver (this), may have an argument (it) and may have a return value. See the differences

    run

    val string = “a”
    val result = string.run {
        // this = “a”
        // it = not available
        1 // Block return value
        // result = 1
    }
    

    let

    val string = “a”
    val result = string.let {
        // this = this@MyClass
        // it = “a”
        2 // Block return value
        // result = 2
    }
    

    with

    val string = “a”
    val result = with(string) {
        // this = “a”
        // it = not available
        3 // Block return value
        // result = 3
    }
    

    apply

    val string = “a”
    val result = string.apply {
        // this = “a”
        // it = not available
        4 // Block return value unused
        // result = “a”
    }
    

    This week covered some more language features, such as interop, refied, and sequences, then we moved on to Android KTX showing off some of the ways it helps you write concise and readable code. To finish off the series we covered the powerful Kotlin scope functions.

    相关文章

      网友评论

          本文标题:31天Kotlin

          本文链接:https://www.haomeiwen.com/subject/wqpjqqtx.html