31天Kotlin

转载: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://upload-images.jianshu.io/upload_images/1742298-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://upload-images.jianshu.io/upload_images/1742298-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.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容