一、 变量和函数

1.1 变量

Kotlin定义变量,只允许在变量前声明两种关键字:val 和 var

  • val(value 的简写)用来声明一个不可变的变量,这种变量在初始赋值之后就再也不能重新赋 值,对应 Java 中的 final 变量。
  • var(variable 的简写)用来声明一个可变的变量,这种变量在初始赋值之后仍然可以再被重新赋值,对应 Java 中的非 final 变量。

kotlin有十分优秀的类型推导机制,你可以不声明变量的类型。
但是如果你使用延迟赋值的话,kotlin的推导机制可能无法正常工作,为此,应该养成显式声明变量类型的习惯:

  1. var number : Int = 10

以上代码含义为:声明了一个Int类型的可变变量number,并为它赋值10。

_* 没错,kotlin中的变量类型和Java的区别就是首字母大写。kotlin放弃使用基本类型,而是使用了Java包装类一样的对象数据类型。 *_

1.2 函数

首先,我们来看一段代码:

  1. fun largerNumber(num1: Int, num2: Int): Int {
  2. return max(num1, num2)
  3. }

emmm…
kotlin定义一个方法(函数)必须使用 fun 关键字;下面详细分析一下
largerNumber 方法名
num1:Int 参数名 :参数类型型
Int 返回值类型

上面是最基础的函数表达式,后续有更复杂的再讲。

在Lambda 盛行的今天,来看看Kotlin可以怎么做:

  • 当一个函数中只有一行代码时,我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可 。

    1. fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)
  • 既然kotlin有自己的推导机制,所以返回值类型也可以省略。 ```kotlin fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

  1. - 如果使用public 方法,则必须明确写出返回类型
  2. ```kotlin
  3. public fun sum(a: Int, b: Int): Int = a + b
  • 如果返回值为空,可以有以下几种写法: ```kotlin fun printSum(a: Int, b: Int): Unit { print(a + b) }

// 如果是返回 Unit类型,则可以省略(对于public方法也是这样): public fun printSum(a: Int, b: Int) { print(a + b) }

  1. - 可变参数,用 `**vararg** `关键字进行标识
  2. ```kotlin
  3. fun vars(vararg v:Int){
  4. for(vt in v){
  5. print(vt)
  6. }
  7. }
  • lambda(匿名函数)

    1. fun main(args: Array<String>) {
    2. val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
    3. println(sumLambda(1,2)) // 输出 3
    4. }

    二、程序的逻辑控制

    三类执行语句:顺序语句、条件语句、循环语句;

    2.1 if条件语句

  • 首先看一下大众的写法: ```kotlin fun largerNumber(num1: Int, num2: Int): Int { var value = 0 if (num1 > num2) {

    1. value = num1

    } else {

    1. value = num2

    } return value }

  1. - kotlin追加了一个特性,使得 if 能够返回值
  2. ```kotlin
  3. fun largerNumber(num1: Int, num2: Int): Int {
  4. val value = if (num1 > num2) {
  5. num1
  6. } else {
  7. num2
  8. }
  9. return value
  10. }
  • 这样一来,value就显得有点多余,我们把它去除

    1. fun largerNumber(num1: Int, num2: Int): Int {
    2. return if (num1 > num2) {
    3. num1
    4. } else {
    5. num2
    6. }
    7. }
  • 一行语句?那么语法糖(Lambda)来了

    1. fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
    2. num1
    3. } else {
    4. num2
    5. }
  • 括号显得有点多余,进行精简 ```kotlin fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

  1. <a name="ByXGV"></a>
  2. ## 2.2 when 语句
  3. when语句允许传入任意类型的参数,然后在结构提中定义一系列条件。<br />` 匹配值 -> { 执行逻辑 } `
  4. - 小试牛刀一下
  5. ```kotlin
  6. fun getScore(name: String) = when (name) {
  7. "Tom" -> 86
  8. "Jim" -> 77
  9. "Jack" -> 95
  10. "Lily" -> 100
  11. else -> 0
  12. }
  • 除了以上这种精准匹配的方式外。kotlin还支持类型匹配

    1. fun checkNumber(num: Number) {
    2. when (num) {
    3. is Int -> println("number is Int")
    4. is Double -> println("number is Double")
    5. else -> println("number not support")
    6. }
    7. }
  • when 也可以不填入判断参数,以此做某些更骚气的操作:如下代码中,当name为Tom开头时,返回86

    1. fun getScore(name: String) = when {
    2. name.startsWith("Tom") -> 86
    3. name == "Jim" -> 77
    4. name == "Jack" -> 95
    5. name == "Lily" -> 100
    6. else -> 0
    7. }

2.3 循环语句

Java中的for-i循环被kotlin舍弃,for-each循环被增强为for-in。

val range = 0..10 表示一个区间[0,10]
val range = 0 until 10 表示一个左闭右开的区间 [0,10)
val range = 10 downTo 1 表示一个降序区间[10,1]

  • 简单写法
    1. fun main() {
    2. for (i in 0..10) {
    3. println(i)
    4. }
    5. }
  • 使用步长 step 关键字 ```kotlin fun main() { for (i in 0 until 10 step 2) { println(i) } }
  1. - 使用降序 downTo 关键字
  2. ```kotlin
  3. fun main() {
  4. for (i in 10 downTo 1) {
  5. println(i)
  6. }
  7. }

三、 面向对象编程

3.1类和对象

3.1.1 类定义

  • class 关键字声明类

    1. class Runoob {
    2. ...
    3. }
  • 定义一个空类

    1. class Empty

    3.1.2 类的属性

  • 属性定义

    1. var <propertyName>[: <PropertyType>] [= <property_initializer>]
    2. [<getter>]
    3. [<setter>]
  • getter和setter

    1. class Person {
    2. var lastName: String = "zhang"
    3. get() = field.toUpperCase() // 将变量赋值后转换为大写
    4. set
    5. var no: Int = 100
    6. get() = field // 后端变量
    7. set(value) {
    8. if (value < 10) { // 如果传入的值小于 10 返回该值
    9. field = value
    10. } else {
    11. field = -1 // 如果传入的值大于等于 10 返回 -1
    12. }
    13. }
    14. var heiht: Float = 145.4f
    15. private set
    16. }
  • Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器。如上。

  • 非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性

    1. public class MyTest {
    2. lateinit var subject: TestSubject
    3. @SetUp fun setup() {
    4. subject = TestSubject()
    5. }
    6. @Test fun test() {
    7. subject.method() // dereference directly
    8. }
    9. }
  • 创建实例

    1. val site = Runoob() // Kotlin 中没有 new 关键字
  • 属性引用 ```kotlin site.name // 使用 . 号来引用 site.url

  1. <a name="mZzn6"></a>
  2. ### 3.1.3 主构造器
  3. 主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
  4. ```kotlin
  5. class Person constructor(firstName: String) {
  6. init {
  7. println("FirstName is $firstName")
  8. }
  9. }

主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。可以通过主构造器来定义属性并初始化属性值(可以是var或val)

  • 创建一个 Runoob类 ```kotlin class Runoob constructor(name: String) { // 类名为 Runoob // 大括号内是类体构成 var url: String = “http://www.runoob.com“ var country: String = “CN” var siteName = name

    init {

    1. println("初始化网站名: ${name}")

    }

    fun printTest() {

    1. println("我是类的函数")

    } }

  1. <a name="osNSu"></a>
  2. ### 3.1.4 次构造函数
  3. 类也可以有二级构造函数,需要加前缀 constructor;<br />如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字
  4. ```kotlin
  5. class Person(val name: String) {
  6. constructor (name: String, age:Int) : this(name) {
  7. // 初始化...
  8. }
  9. }
  1. class Runoob constructor(name: String) { // 类名为 Runoob
  2. // 大括号内是类体构成
  3. var url: String = "http://www.runoob.com"
  4. var country: String = "CN"
  5. var siteName = name
  6. init {
  7. println("初始化网站名: ${name}")
  8. }
  9. // 次构造函数
  10. constructor (name: String, alexa: Int) : this(name) {
  11. println("Alexa 排名 $alexa")
  12. }
  13. fun printTest() {
  14. println("我是类的函数")
  15. }
  16. }

3.1.5 类的修饰符

  • 类属性修饰符
    • abstract // 抽象类
    • final // 类不可继承,默认属性
    • enum // 枚举类
    • open // 类可继承,类默认是final的
    • annotation // 注解类
  • 访问权限修饰符

    • private // 仅在同一个文件中可见
    • protected // 同一个文件中或子类可见
    • public // 所有调用的地方都可见
    • internal // 同一个模块中可见

      3.1.6 各种类的变形

  • 抽象类 ```kotlin open class Base { open fun f() {} }

abstract class Derived : Base() { override abstract fun f() }

  1. - 嵌套类
  2. ```kotlin
  3. class Outer { // 外部类
  4. private val bar: Int = 1
  5. class Nested { // 嵌套类
  6. fun foo() = 2
  7. }
  8. }
  9. fun main(args: Array<String>) {
  10. val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
  11. println(demo) // == 2
  12. }
  • 内部类 ```kotlin class Outer { private val bar: Int = 1 var v = “成员属性” /嵌套内部类/ inner class Inner {
    1. fun foo() = bar // 访问外部类成员
    2. fun innerTest() {
    3. var o = this@Outer //获取外部类的成员变量
    4. println("内部类可以引用外部类的成员,例如:" + o.v)
    5. }
    } }

fun main(args: Array) { val demo = Outer().Inner().foo() println(demo) // 1 val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性 }

  1. - 匿名内部类
  2. ```kotlin
  3. class Test {
  4. var v = "成员属性"
  5. fun setInterFace(test: TestInterFace) {
  6. test.test()
  7. }
  8. }
  9. /**
  10. * 定义接口
  11. */
  12. interface TestInterFace {
  13. fun test()
  14. }
  15. fun main(args: Array<String>) {
  16. var test = Test()
  17. /**
  18. * 采用对象表达式来创建接口对象,即匿名内部类的实例。
  19. */
  20. test.setInterFace(object : TestInterFace {
  21. override fun test() {
  22. println("对象表达式创建匿名内部类的实例")
  23. }
  24. })
  25. }

3.2 继承

Kotlin 中所有类都继承 Any 类,且提供三个方法

  1. equals()
  2. hashCode()
  3. toString()

在kotlin中,任何一个非抽象类默认都是不能被继承的,除非加入 open 关键字。 遵循Java编程规范

  • 定义一个可被继承的父类 Person ```kotlin open class Person{ var name = “” var age = 0 fun eat() {
    1. println(name + " is eating. He is " + age + " years old.")
    2. }
    }
  1. Java中的 extends 关键字不同的是,kotlin使用的是 : (冒号)。
  2. - 定义一个继承了Person的子类Student
  3. ```kotlin
  4. class Student : Person(){
  5. var sno = ""
  6. var grade = 0
  7. }

3.2.1 重写

方法默认也是final的,如果允许子类重写,则需要加上open关键字。子类重写时加上override关键字。

  1. /**用户基类**/
  2. open class Person{
  3. open fun study(){ // 允许子类重写
  4. println("我毕业了")
  5. }
  6. }
  7. /**子类继承 Person 类**/
  8. class Student : Person() {
  9. override fun study(){ // 重写方法
  10. println("我在读大学")
  11. }
  12. }
  13. fun main(args: Array<String>) {
  14. val s = Student()
  15. s.study();
  16. }
  • 如果是多继承的情况(接口等),有多个同名方法,可以使用super泛型进行区分。并且,为了消除歧义,子类也必须重写同名方法 ```kotlin open class A { open fun f () { print(“A”) } fun a() { print(“a”) } }

interface B { fun f() { print(“B”) } //接口的成员变量默认是 open 的 fun b() { print(“b”) } }

class C() : A() , B{ override fun f() { super.f()//调用 A.f() super.f()//调用 B.f() } }

fun main(args: Array) { val c = C() c.f(); }

  1. <a name="DSQ6I"></a>
  2. ### 3.2.2 属性重写
  3. 同理,实现属性重写也必须使用open和override关键字。重写的属性需要具有兼容性。<br />**可以使用var重写val,但不能使用val重写var,因为val本身定义了getter方法,重写为var会多出一个setter方法。**
  4. <a name="c3cjh"></a>
  5. ## 3.3 接口
  6. kotlin接口的写法和Java8类似,使用interface关键字定义,允许方法有默认实现。
  7. ```kotlin
  8. interface MyInterface {
  9. fun bar() // 未实现
  10. fun foo() { //已实现
  11. // 可选的方法体
  12. println("foo")
  13. }
  14. }
  • 接口实现 ```kotlin interface MyInterface { fun bar() fun foo() {
    1. // 可选的方法体
    2. println("foo")
    } } class Child : MyInterface { override fun bar() {
    1. // 方法体
    2. println("bar")
    } } fun main(args: Array) { val c = Child() c.foo(); c.bar(); }
  1. <a name="LwKO5"></a>
  2. ## 3.4 kotlin拓展
  3. Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
  4. <br />扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
  5. <a name="X1FzP"></a>
  6. ### 3.4.1 拓展函数
  7. 形式:
  8. ```kotlin
  9. fun receiverType.functionName(params){
  10. body
  11. }
  12. params:扩展函数的参数,可以为NULL
  • receiverType:表示函数的接收者,也就是函数扩展的对象