Kotlin 31 Days

作者: WangGavin | 来源:发表于2018-07-01 10:45 被阅读16次

Day1 可见性

参考

31 天,从浅到深轻松学习 Kotlin
Kotlin实战

在 Kotlin 中一切都是默认 public 的。在Kotlin中,存在private、protected、internal和 public四种修饰符,它们可用于修饰类、对象、接口、构造器、函数、属性、以及属性的设值方法等。

// 默认public
val isVisible = true
// 只有在相同源文件内可见
private val isHidden = true
// 同一模块内可见
internal val almostVisible = true

class Foo{
    // 默认public
    val isVisible = true
    // 只能被本类或其子类访问
    protected val isInheritable = true
    // 只能被本类访问
    private val isHidden = true
    // 同一模块可见
    internal val isMan = true
}
修饰符 类成员 顶层声明
public(默认) 所有地方可见 所有地方可见
internal 模块中可见 模块中可见
protected 子类中可见 -
private 类中可见 文件中可见

内部类和嵌套类:默认是嵌套类

类A在另一个类B中声明 在Java中 在Kotlin中
嵌套类(不存储外部类的引用) static class A class A
内部类(存贮外部类的引用) class A inner Class A
/**
 * 内部类
 */
class Outer{
    inner class Inner{
        fun getOuterReference(): Outer = this@Outer //获取外部类的引用
    }
}

Day2 可空性

参考

Kotlin Reference: Null Safety
Kotlin教程(四)可空性

可空类型与非空类型

如果一个变量可能为空,然后对这个变量直接进行调用是不安全的,很容易造成NullPointerException,通常我们的办法是加个if判断,但是用java写个if在Kotliln看来语句也是听多的。所以Kotlin给变量增加了可空类型和非空类型特性。

通常声明一个变量,如果变量类型后面没有加?,那么它是非空类型,也就是它不能为null

例子:


没有加?是非空类型

非空类型变量a赋值为null,直接爆红

但是我加了?就正常了,证明加了?表示这个变量为可空类型,可以赋值为null

加?是可空类型

非空判断与安全调用

加? 和不加可以看做是两种类型,不能直接调用可空类型的方法,只有与null进行比较后,编译器才会智能转换这个类型。
例如:

fun strLen(s: String?) = if(s != null) s.length else 0  

这样s才能转为非空类型,才能调用其length属性

为了简化是否为null的if语句,Kotlin提供了?.这个安全调用运算符,它把一次null检查和一次方法调用合并成一个操作,例如例如表达式s?.toUpperCase() 等同于if (s != null) s.toUpperCase() else null,换句话说,如果你试图调用一个非空值的方法,这次方法调用会被正常地执行。但如果值是null,这次调用不会发生,而整个表达式的值为null。因此表达式s?.toUpperCase() 的返回类型是String?

安全调用支持链式调用,很实用,例:

bob?.department?.head?.name

Elvis 操作符

对于一个可为空的引用 r,我们可以说“如果 r 不为空,则使用它,否则使用另外的非空值 x”
原来写法:

val l: Int = if (b != null) b.length else -1

使用Elvis写法:

val l: Int = b?.length ?: -1

如果 ?: 左边的表达式不为空,则 Elvis 操作符返回该表达式;否则返回右边的表达式。注意只有在左边的表达式不为空时,才会计算右边的表达式。

因为在 Kotlin 中 throw 和 return 也属于表达式,所以它们也可以放在 Elvis 操作符的右边。这一点非常方便,如在检查函数参数时:

fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ...
}

!!操作符

如果你想要看到NPE,就是让他像用Java一样可能抛NPE,那么你要显示的在变量后面加!!,不过这样的情况一般是在你100%确定该变量不会为空。
例如:

var a: String? = "abc" 
 var b: Int = a!!.length

安全转换

常规的转换会在对象不属于目标类型时导致 ClassCastException。此外还可以使用安全转换,安全转换会在转换失败时返回 null:

val aInt: Int? = a as? Int

Day3 String模板

格式化字符串?将$放在变量名的前面去表达字符串中的变量和表达式。使用 ${expression} 求表达式的值。
例子:

    val i = 21
    val s = "I'm $i years old! "
    println(s)
    val b = 2
    val s2 = "after $b years,I'm ${i+b} years old!"
    println(s2)

Day4 when表达式

参考文章

31 天,从浅到深轻松学习 Kotlin
kotlin中的when:强大的switch

when相当于switch,但是Kotlin的when是个强大的switch,几乎可以匹配任何东西,字面值,枚举,数组范围

像用Java的switch一样使用

when(条件){
  条件值1 -> 执行语句1
  条件值2 -> 执行语句2
  条件值3 -> 执行语句3
  else -> 执行语句4
}

自动转换类型

 when (view) {
     is TextView -> toast(view.text)
     is RecyclerView -> toast("Item count = ${view.adapter.itemCount}")
     is SearchView -> toast("Current query: ${view.query}")
     else -> toast("View type not supported")
 }

无自变量when,类似if else的含义,可做表达式

 val res = when {
     x in 1..10 -> "cheap"
     s.contains("hello") -> "it's a welcome!"
     v is ViewGroup -> "child count: ${v.getChildCount()}"
     else -> ""
 }

Day5 循环,范围表达式与解构

    for(i in 1..100){
    ...
    }
    for(i in 100 downTo 1){
    ...
    }
    val array = arrayOf("a","b","c")
    for (i in 1 until array.size step 2){
        println(i)
    }
    for ((index,element) in array.withIndex()){
    ...
    }

    val map = mapOf(1 to "one", 2 to "two")
    for ((key,value) in map){
    ...
    }

Day6 属性

再Kotlin中,类可以具有可变和只读属性,默认情况下生成getter和setter,也可以自定义。

 class User{
     // 只读属性
     val id: String = ""
     // 可变属性,默认有getter和setter
     var name: String = ""

     var surname: String = ""
     get() = surname.toUpperCase()//自定义getter

     var email: String = ""
     set(value){// 自定义setter
         if (isEmailValid(value)) field = value
     }
 }

Day7 解构

Android KTX 使用解构来分配颜色的组件值。您可以在您的类中使用解构,或者扩展现有的类来添加解构。

    val (red,green,blue) = color
    val (left,top,right,bottom) = react
    val (x,y) = point

解构声明可以一次声明多个变量,任何表达式都可以出现在解构声明的右侧,只要我们可以对它调用所需数量的component函数(注意:componentN()函数需要用operator关键字修饰,以允许其在解构声明声明中使用它们)。数据类自动声明component函数。

data class Person(val name: String = "Kotlin",val sex: String = "男",val age: Int = 20)
fun foo(){
    var (name,sex,age) = Person()
    var (_,sex2,age2) = Person()
    println("name:$name,sex:$sex,age:$age")
}

也可以在for循环中使用解构声明,只要集合中的每个元素提供有componentN()方法

var collections= arrayListOf<Person>()//list集合中元素要为其每个成员属性提供有componentN方法
    for((name,sex,age) in collections){
        println("name=$name&sex=$sex&age=$age")
    }

也可以对映射使用解构赋值。因为map结构提供函数 component1() 和 component2() 来将每个元素呈现为一对。

    var map= mutableMapOf<String,String>()
    map.put("name","Kotlin")
    map.put("sex","男")
    map.put("age","13")
    for ((key,value) in map){
        println("$key=$value")
    }

Day 8 简单的bundle

通过简洁的方式创建bundle,不调用putString,putInt,或它们的20个方法中的任何一个。一个调用生成一个新的bundle,甚至可以处理Arrays,bundle很像map

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)
)

val testMap = mapOf(
            "int" to 1,
            "long" to 2L,
            "boolean" to true,
            "null" to null,
            "array" to arrayOf(1,2)
    )

Day 9 Parcelize

习惯Parcelable的速度,但不喜欢写所有的代码?和@Parcelize打个招呼.

@Parcelize
data class User(val name: String, val occupation: Work): Parcelable

// build.gradle
androidExtensions {
  // enale experimental kotlin features in gradle to enable Parcelize
    experimental = true
}

Day10 Data类和equality

可以创建一数据类,它可以默认实现生成equals()方法相当于hashCode(),toString()和copy(),并检查结构是否相等。

fun main(args: Array<String>){
     val user1 = User("wang","123456","12345")
    val user2 = User("wang","123456","12345")
    println("user1 equals user2 :${user1 == user2}")
}


data class User(val name: String,
                val email: String,
                val address: String)

Day11 简化postDelay

Android KTX已经为Handler做了个小包装,类似于传闭包的形式传入参数。

// android KTX api
fun Handler.postDelayed(
  delay:Int,
  token: Any? = null,
  action: () -> Unit
)

handle.postDelayed(50){
  // pass a lambda to postDelayed
}

Day12 默认参数

如果方法中的参数太多,Kotlin可以在函数中指定默认参数值,使用命名参数使代码更具有可读性。

class BulletPointSpan(
  private val bulletRadius: Float = DEFAULT_RADIUS,
  private val gapWidth: Int = DEFAULT_GAP_WIDTH,
  private val color: Int = Color.BLACK
){...}
// 只用默认值
val bulletPointSpan = BulletPointSpan()

// 传第一个参数值
val bulletPointSpan2 = BulletPointSpan(resources.getDimension(R.dimen.radius))
// 传最后一个参数的值
val bulletPointSpan3 = BulletPointSpan(color = Color.RED)

Day13 java调用Kotlin

java和,Kotlin混用,默认情况下,编译器可以生成顶级类,名称为YourFileKt,可以通过使用@file:JvmName注释文件来更改它。

ShapesGenerator.kt

package com.newtrekwang.kotlinprac
fun generateCicir():String{
    return "circle"
}

fun generateRect():String{
    return "rect"
}

val WIDTH : Int = 100
var height: Int = 300

java调用

 public static void main(String argv[]){
          String circle =   ShapesGeneratorKt.generateCicir();
          int width = ShapesGeneratorKt.getWIDTH();
          int height = ShapesGeneratorKt.getHeight();
          ShapesGeneratorKt.setHeight(200);
        }

使用file注解

@file:JvmName("Test")
package com.newtrekwang.kotlinprac

fun generateCicir():String{
    return "circle"
}

fun generateRect():String{
    return "rect"
}

val WIDTH : Int = 100
var height: Int = 300

java调用

  public static void main(String argv[]){
          String circle =   Test.generateCicir();
          int width = Test.getWIDTH();
          int height = Test.getHeight();
         Test.setHeight(200);
        }

Day14 在没有迭代器的情况下迭代类型

迭代器用在了有趣的地方!Android KTX 将迭代器添加到 viewGroup 和 sparseArray。要定义迭代器扩展请使用 operator 关键字。 Foreach 循环将使用扩展名!
可惜androidKTX要最新版开发版AS才能用。

// Andrfoid KTX
for(view in viewGroup){}
for(key in sparseArray){}

// 你的project
operator Waterfall.iterator(){
  // 添加一个迭代器给waterFall类
}
for(item in myClass){}

第二周小结:
这周我们更深入的学习了 Kotlin 的特性:简洁 bundle,迭代,Data,postDelay,默认参数,序列化。

Day 15 Sealed Class

Kotlin的sealed类可以让你轻松的处理错误数据

密封类的好处在于:使用when表达式,如果能覆盖所有情况,就无需再添加else子句

sealed class NetworkResult
data class Success(val result: String):NetworkResult()
data class Failure(val error: Error):NetworkResult()

fun useSealedClass(networkResult: NetworkResult){
    when(networkResult){
        is Success -> showResult(networkResult.result)
        is Failure -> showError(networkResult.error)
    }
}

如果将sealed类用在RecyclerView的Adapter中,非常适合于ViewHolders,用一组干净的类型明确地分派给每个持有者。用作表达式时,如果有类型不匹配,编译器将会出错。

override fun onBindViewHolder(holder: SealedAdapterViewHolder?,position: Int){
  when(holder){
      is HeaderHolder -> {...}
      is DetailHolder -> {....}
  }
}

使用RecyclerView,如果我们有很多来自RecyclerView中的item的回调,比如一个点击,分享和删除item的项目,我们可以用sealed类,一个回调处理所有的事情!

sealed class DetailItemClickEvent
data class DetailBodyClick(val section: Int): DetailItemClickEvent()
data class ShareClick(val platform: String): DetailItemClickEvent()
data class DeleteClick(val confirmed: Boolean): DetailItemClickEvent()

interface ItemOnClickListener{
    fun onClick(detailItemClickEvent: DetailItemClickEvent)
}

class MyHander: ItemOnClickListener{
    override fun onClick(detailItemClickEvent: DetailItemClickEvent) {
        when(detailItemClickEvent){
            is DetailBodyClick -> {...}
            is ShareClick -> {...}
            is DeleteClick -> {...}
        }
    }
}

Day 16 懒加载

通过使用懒加载,可以省去昂贵的属性初始化的成本直到它们真正需要时。计算值然后保存并为了未来的任何时候的调用。

lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行。

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

Day 17 lateinit

在kotlin中不为空的对象必须初始化,var 变量加这个关键字表示这个变量迟早会初始化,不过lateinit只能用于var

例如,Activity里的成员初始化

class MyActivity: AppCompatActivity(){
    lateinit var recyclerView: RecyclerView
 override fun onCreate(savedInstanceState: Bundler?){
     recyclerView = findViewById(R.id.recycler_view)
   }
}

Day18 要求(require)和检查(check)

判断方法参数的有效,可以用require在使用前检查它们,如果它们是无效的,将会抛出IllegalArgumentException

fun setName(name: String){
    require(name.isNotEmpty()){ "Invalid name" }
}

封闭类的状态是否正确,可以使用check来验证,如果检查的值为false,它将抛出IllegalStateException

fun logout(){
    check(isLogin()){"没有登录!"}
    //todo
}

本质就是Kotlin标准库定义了个require和check含闭包的方法

相关文章

网友评论

    本文标题:Kotlin 31 Days

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