Kotlin For Android 笔记(二)

2019-03-04 10:44:18 浏览数 (1)

1、当方法参数不定的时候,我们可以使用 vararg 声明,这样就可以传入多个值了;但每个方法只能声明一个 vararg
代码语言:javascript复制
fun printSum(vararg numbers: Int) {
    val sum = numbers.sum()
    print(sum)
}
printSum(1,2,3,4,5) // Prints: 15
printSum() // Prints: 0

// 可以直接传递数组(但是要以 * 为前缀)
val texts=arrayOf("b","c","d")
printAll(*texts)
2、当 when 的参数为空时,可以省略 ()
代码语言:javascript复制
                                                  // 这里省略了 `()`
override fun onOptionsItemSelected(item: MenuItem?)=when{
        item?.itemId==R.id.design_menu_item_action_area->{
            onBackPressed()
            true
        }
        else -> super.onOptionsItemSelected(item)
}
3、Default arguments value versus Named arguments syntax
代码语言:javascript复制
fun printValue(value: String, inBracket: Boolean = true,prefix: String = "", suffix: String = "") {
    print(prefix)
    if (inBracket) {
        print("(${value})")
     } else {
        print(value)
    }
    println(suffix)
}

printValue("str", true, "","") // Prints: (str)
printValue("str") // Prints: (str)
printValue("str", false) // Prints: st

//命名式参数
printValue("str", suffix = "!") // Prints: (str)!
四、Top-level function

使用 Android 项目,Kotlin 被编译成运行在 Dalvik 上的 Java 字节码。虚拟机(安卓 5.0 之前)或安卓运行时(安卓 5.0 及更高版本)。两个虚拟机都只能执行类内定义的代码。为了解决这个问题,Kotlin 编译器为顶级函数生成类。这个类名由 文件名kt 后缀构成。在这样的 class 里函数和属性是静态的。例如,假设我们定义了 printer.kt 文件中的函数:

代码语言:javascript复制
// Printer.kt
fun printTwo() {
    print(2)
}

将 kotlin 代码编译成 Java 字节码。生成的字节码将类似于下面的 Java 类生成的代码:

代码语言:javascript复制
//Java
public final class PrinterKt { // 1
  public static void printTwo() { // 2
      System.out.print(2); // 3
    }
}

1、printerkt 是由 文件名kt 后缀组成的名称。 2、所有顶级函数属性都编译为静态方法和变量。 3、print 是一个 kotlin 函数,但由于它是一个内联函数,因此它的调用被替换为 它在编译期间的主体。它的主体只包含 system.out.println 的调用

我们还可以在 Java 文件中访问 Kotlin 顶级函数。用类名前缀函数调用:

代码语言:javascript复制
//Java file, call inside some method
PrinterKt.printTwo()

正如我们所看到的,Kotlin 与 Java 是可以互操作的。为了使 Kotlin 的顶级方法,在 Java 中使用更加方便,我们可以添加一个可以更改名称的 annotation (这个注解一定要放在 package 的上面) 一个 JVM 生成的类。这样在 Java 中调用 Kotlin 的顶级方法和属性是很方便的。类似下面这样:

代码语言:javascript复制
@file:JvmName("Printer")

接着就可以在 Java 文件使用了。Printer.printTwo()

五、Local functions(局部方法)
代码语言:javascript复制
fun makeStudentList(): List<Student> {
    var students: List<Student> = emptyList()
  fun addStudent(name: String, state: Student.State =  Student.State.New) {
    students  = Student(name, state, courses = emptyList())
  }
  // ...
  addStudent("Adam Smith")
  addStudent("Donald Duck")
  // ...
  return students
}

这样写有一个好处就是,可以直接使用当前方法的变量,不需要去传递;如果把它提取为成员方法,那就需要传递所需要的参数,如果调用修改了参数的形式,那函数的声明也需要重新修改,这样就导致了要修改两个地方(Java 中经常出现这样的问题)。

六、自定义 getter/setter
代码语言:javascript复制
class Student {
    var age: Int?//自定义 get/set 方法
        get() = age
        set(value) {//value 是自定义的变量名,名字可以随便起
            age = value
        }
}

image.png

这样会爆出堆栈错误!!!让我们来看一下 Student 的 class 文件

image.png

果然,造成了堆栈溢出(recursive call)

这时候使用 Kotlin 提供的备用字段(关键字 field)即可解决这个问题

代码语言:javascript复制
class Student {
    var age: Int? = 0
        get() = field //使用备用字段自定义 get/set 方法
        set(value) {
            field = value
        }
}

再次查看 class 文件发现代码也正常了

image.png

备用字段 field 的使用范围仅限于当前属性的 get/set 方法。

以下两种情况不支持 field (写了也没效果)字段

① 因为第一次类创建的时候,属性的值就被计算了(后续的值不会再起作用)

代码语言:javascript复制
class Fruit(var weight: Double) {
    val isHeavy = weight > 20
}
    var fruit = Fruit(7.0)
    println(fruit.isHeavy) // Prints: false
    fruit.weight = 30.5
    println(fruit.isHeavy) // Prints: false

② 因为它的值总是使用另一个属性计算的。

代码语言:javascript复制
class Car {
    var usable: Boolean = true
    var inGoodState: Boolean = true
    var crashed: Boolean
    get() = !usable && !inGoodState
    set(value) {
      usable = false
      inGoodState = false
  }
}

如果类没有主构造函数,而超类有一个非空的构造函数,然后每个次构造函数必须使用 super 关键:

代码语言:javascript复制
class ProductView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs : AttributeSet) super(ctx, attrs)
}

0 人点赞