第一章 Kotlin基础语法

一、常量与变量(val,var)

  • 1.什么是常量?

1 .val = value ,值类型;
2.类似Java的final;
3.不能重复赋值;
4.举例:
运行时常量:val x = getX(); 也就是程序运行时才赋值;
编译期常量:const val x = 2 ;也就是在编译时期就赋值了;

  • 2.什么是变量?

1.var=variable ;
2.可以再次赋值;
3.举例:
var x = "HelloWorld"//定义变量
x = "HiWorld"//再次赋值

示例如下:

/**
 * 常量
 * 带const为编译期的常量
 * 不带const的val为运行期常量
 * 不能再次赋值
 */
const val FINAL_HELLO_WORLD:String = "Hello world"

//变量 能够再次赋值
var helloworld:String = FINAL_HELLO_WORLD

二、类型推导

val string = "Hello"//推导出String类型
val int = 5 //Int类型
var x = getString()+5 //String类型,加号在这里是连接符

三、函数--单一原则:

1.任何函数都是以fun开头的,紧接着的是方法名字,紧接着的是参数列表,紧接着的是返回值,紧接着是函数体;
fun (:):{}
2.如果无返回值,可以写:Unit或者在参数列表后什么都写;Unit类型Java中的void;
3.如果参数有多个,以逗号分开;
4.如果一个函数只是返回一个函数的表达式的值,直接可以等于表达式即可;
5.函数的名字不是必须的,如果有个变量接受返回值即可编译通过;--请看下面的例子
6.函数作为类型的一种出现的,可以被用来赋值与传递的;

/**
 * 函数
 * methodName
 * param1,param2是参数名
 * String Int 是参数类型
 * 备注:
 * 1.任何函数都是以fun开头的;
 * 2.如果参数有多个,以逗号分开;
 * 3没有返回值在参数后添加:Unit,也可以不写,这个Unit相当于java里面的void;
 * 4如果一个函数只是返回一个函数的表达式的值,直接可以等于表达式即可;
 */
fun methodName(param1:String,param2:Int):Unit {

}

//带返回值的函数
fun testMethod(args:String):String{
    return args
}

/**
 * 如果一个函数只是返回一个函数的表达式的值,直接可以等于表达式即可
 */
fun returnFun0(param1: Int, param2: Int):Int {
    return param1+param2
}
//上面的等价于下面的写法:
fun returnFun(param1:Int,param2:Int) = param1+param2

/**
 * 函数可以没有名字的,如果有一个变量来接收的话即可
 */
val resultValue = fun(x: Int):Int {
    return x
}

四、Lambda表达式

1.也就是匿名函数;函数作为类型的一种出现的,可以被用来赋值与传递的;
2.写法:{[参数列表] ->[函数体,最后一行是返回值]}
3.() -> Unit
--无参,返回值为Unit;
4.(Int) -> Int
--有参-传入Int类型,返回一个整数;
5.(String,(String) -> String) -> Boolean
--传入字符串、Lambda表达式,返回Boolean;

  • 1.Lambda表达式的调用

1.用括号()来表示方法的调用,是Kotlin中的运算符,等价于invoke();这个invoke其实是运算符重载的方法
2.Lambda表达式如果没有参数,箭头 (->)就不用写了;
3.Lambda表达式不是只有一行的,它的返回值是它表达式的最后一行的值;

val sum = {a:Int,b:Int ->a+b}
//用括号()来调用,等价于invoke();
sum(2,3)
或者使用:sum.invoke(2,3),二者完全等价;

//Lambda表达式没有参数,箭头可以不写,如下:
val printHello = {
  println("hello")//相当于函数的函数体
}

//Lambda表达式不是只有一行的,它的返回值是它表达式的最后一行的值
val sum = {a:Int,b:Int ->
  println("$a + $b = ${a+b}")//这一行也是表达式,不过返回的是Unit类型
  a+b//这一行最为Lambda的最后一行,它的值也是Lambda的返回值;
}
  • 2.Lambda表达式的简化
  • 1.函数参数调用时最后一个Lambda可以移出去;也就是说如果一个函数调用时,最后一个参数是Lambda表达式,我们传入参数时,那么这个参数可以被移出去;
  • 2.函数的参数只有一个Lambda,调用时小括号可以省略;
  • 3.如果一个Lambda只有一个参数,可以不写,可默认为it;
  • 4.入参、返回值与形参一致的函数可以用函数引用的方式作为实参传入;
val sum = {a:Int,b:Int ->a+b}
//Lambda表达式在没有参数时,箭头就不用写了,如下:
//如下面的写法,因为没有参数,箭头就不用写了:
val printHello = {
   println("hello")//相当于函数的函数体
}

//看下面的数组遍历:
arg.forEach{element -> println(element) }
//如果一个Lambda传入的参数只有一个,可以不写,可默认为it;上面的等价于:
arg.forEach{ println(it) }
//函数的参数只有一个Lambda表达式时,调用时可以将大括号移到小括号外面;
//上面的等价于下面的:
arg.forEach(){println(it)}
//这个小括号没有什么用,可以把这个小括号省略
arg.forEach{println(it)}
//如果函数的参数和返回值与action的参数和返回类型一致,就可以用函数引用的方式,如下:
arg.forEach(::println)

五、循环语句

for循环如下:


    for (i in args) {
        println("输出的是:$i")
    }


    for ((index, value) in args.withIndex()) {
        println("$index -> $value")
    }

    for (indexValue in args.withIndex()) {
        println("${indexValue.index}---->${indexValue.value}")
    }
//或者使用下面的方式
   args.forEach{
     println(it)
   }
}

//看下面的一个方法,return会把函数截断,无法执行到最后一行
fun main(args: Array) {

    args.forEach {
        if (it == "c") return
        println(it)
    }
    //你会发现如果上面的if条件触发了 ,将跳出for循环,并且执行不到下面的代码,原因是:
    //上面的是表达式,而不是一个简单的遍历,return会调出整个函数的执行
    println("The End")
}
//如果想只跳出it=="c"这个条件,循环继续进行,整个函数执行完毕,可以如下:
fun main(args: Array) {

    args.forEach ForEach@ {
        if (it == "c") return@ForEach
        println(it)
    }
    println("The End")
}

while循环

var int = 5 
    while (int > 0) {
        println("int---->$int")
        int-- 
    }
    
    do {
        println("int---->$int")
        int--
    }while (int>5)

如何跳出 或 跳过循环?
跳出或终止循环:break
跳过循环:continue
多层嵌套循环的终止需要结合标签来实现

 Outter@ for (i in args) {

        Inner@while (i.toInt() > 0) {
            break@Outter  //这里只是跳出外层的for循环 break和@Outter之间不能有空格
        }
    }

六、异常捕获(try catch finally)

kotlin里面的异常捕获和Java中的异常捕获用法一样,最后都会调用finally;但Kotlin中的try、catch也是表达式,这点与Java中的不一样,它可以有返回值,返回值是表达式的最后一行的值;

  • catch分支匹配异常类型;
  • 它是一个表达式,可以用来赋值,与if else when 表达式是完全类似的;
  • finally 无论try catch 是否抛出异常都会执行到;

注意此行代码代表的含义:
return try{ x/y } catch(e:Exception){ 0 } finally{...}
代表:先执行finally里面的代码,再返回;如果无异常,返回x/y;如果有异常,返回0;

  //kotlin中的try catch也是表达式,可以有返回值;返回值是表达式的最后一行的值;
  val result = try {
        args[0]
    } catch (e: Exception) {
        println("jinlaile")
        0
    } finally {
        println("进入finally代码块")
    }
    println(result)

输出结果:
jinlaile
进入finally代码块
0

七、成员方法、成员变量

  • 属性:或者说是成员变量,类范围内的变量;构造方法中val/var修饰的都是属性;类内部也是可以定义属性的的;
/**
 * aField和anotherField是类Hello的两个属性;
 * 而notField不是属性,只是普通构造方法的参数;
 */
class Hello(val aField: Int, notField: Int){
    val anotherField:String = "hello"
}
  • 方法:或者说是成员函数,类范围内的函数;
    在kotlin的类中,自动为var修饰成员变量实现了get、set方法,val修饰的只有get方法,因为val修饰的是不可变的;如果想要在get、set方法中注入部分逻辑,需要我们重写其get、set方法,如下;
/**
 * Created by serenitynanian on 2017/6/27.
 */
class ClassMemberAndField {

    //默认访问控制是public
    var b = 0

    get() {
        println("执行打印逻辑")
        //这个filed其实是上面b后真正的值0,这个filed只有在get、set方法中才能访问的到
        return field
    }
        /**
         * 类型java中的set方法
         * 
         * public void setB(int b){
         *      
         *      this.b = b ;
         * }
         */
    set(value) {
        println("执行打印逻辑")
        field = value
    }
}

如果想要控制get、set的访问权限,可以在get、set方法前加访问限制符,比如protected

  protected set(value) {
            println("logic")
        field = value
    }

如果说get、set中没有部分逻辑,只是想要修改访问控制符,只需要如下写即可:

class ClassMemberAndField {

    //默认访问控制是public
    protected var b = 0

//    get() {
//        //这个filed其实是上面b后真正的值0,这个filed只有在get、set方法中才能访问的到
//        return field
//    }
//        /**
//         * 类型java中的set方法
//         *
//         * public void setB(int b){
//         *
//         *      this.b = b ;
//         * }
//         */
//    protected set(value) {
//            println("logic")
//        field = value
//    }
   
//如果只想要控制访问权限,只需要这样写即可:
    protected get
    
    protected set

}

在kotlin中声明成员变量时,编译器提醒你必须初始化,否则报错;与java不同的时,java会自动给你初始化初始值;但初始值也占内存的,所以有时我们只需要在使用时才想初始化,这样怎么办呢?
只需要在声明成员变量时指定一个lateinit特殊字符就行了;但是在使用之前必须进行初始化,否则报错;

var age:Int = 0 
    //如果不指定lateinit 编译器会提醒必须初始化或者abstract,
    //指定后就是告诉编译器,我后面会进行初始化;
    //如果你在初始化它之前就使用这个变量,会报错的;
    lateinit var name:String

但是lateinit只能放在var修饰的可变变量前面,不能放在val修饰的变量前面;如果想要达到延迟初始化必须使用delegate,如下:

   /**
     * lazy需要传入一个lambda表达式,一个无参的表达式返回一个T类型,
     * 这个T类型就是下面的String类型;
     */
   val address:String by lazy {
       "dsfa"
   }

总结下属性访问控制:
var修饰的有get、set方法;
val修饰的只有get方法,没有set方法,因为val修饰的变量是不可变的;

总结下属性初始化:
属性的初始化尽量在构造方法中进行初始化完成;
无法在构造方法中初始化,尝试降级为局部变量;
var用lateinit延迟初始化,val用lazy;
可空类型谨慎使用null直接初始化;

八、表达式(中缀表达式、分支表达式、when表达式)

  • 中缀表达式
  • 只有一个参数,且用infix修饰的函数;
class Book {
   infix fun on(place: String) {

    }
}

fun main(args: Array) {
    //函数前没有使用infix关键字,必须像平常调用函数一样使用
    Book().on("my desk")
    
    //如果函数前面使用了infix,不用在方法前加. 不用在方法后加括号
    Book() on "my dest"
}
  • 2.使用了中缀表达式后,在调用时,可以直接使用[对象] <函数名> [参数对象] 如上示例;

  • 分支表达式(if),而不只是分支语句
    分支表达式是可以有返回值的,返回值是分支语句中最后一行的值 ;

private const val DEBUG = 1
private const val USER = 0
fun main(args: Array) {

//    var mode = USER
//    if (args.isNotEmpty() && args[0] == "1") {
//        mode = DEBUG
//    }

    /**
     * 从下面可以看出来if可不只是一个简单的分支语句,
     * 它还是一个表达式,它是有返回值的,返回值是是分支表达式中最后一行的值;
     * 在上面的分支语句中,只能定义var的mode,因为后面要修改;
     * 如果使用分支表达式,就直接可以使用val修饰的mode了,使用如下:
     */
    val mode =
            if (args.isNotEmpty() && args[0] == "1") {
                DEBUG
            }else{
                USER
            }

    println("请输入用户名:")
    val username = readLine()
    println("请输入密码:")
    val password = readLine()

    if (mode == DEBUG && username == USERNAME && password == PASSWORD) {
        println("超管登录成功")
    } else if (username == USERNAME && password == PASSWORD) {
        println("普通用户登录成功")
    } else {
        println("登录失败")
    }

}
  • when表达式
    类似Java中的switch语句,由于case语句中只能判断String,enum,byte,int等,并且在每个分支语句中都要写break;在kotlin中没有了switch语句了,使用了when表达式来代替了;
    public void pause() {
        switch (state) {
            case BUFFERING:
            case PLAYING:
                doPause();
                break;
            default:
                //什么都不做
        }
    }

//下面是kotlin中的when表达式
    fun pause() {
        when (state) {
            PlayerKt.State.BUFFERING, PlayerKt.State.PLAYING -> doPause()
            else -> {

            }
        }
    }

when分支表达式只要第一个分支语句执行了,不用使用break,后面的分支语句就不会执行了

fun main(args: Array) {
    var x = 5
    //when表达式只要第一个分支语句执行了,不用使用break,后面的就不会执行了
    when (x) {
        is Int -> println("Hello $x")
        in 1..100 -> println("$x is in 1..100")//x的值是否在[1,100]
        !in 1..100 -> println("$x is not in 1..100")//x的值不在[1,100]
        in 1 until 5 -> println("$x is in[0,5)")//x的值是否在[0,5)之间,取不到5
        args[0].toInt() -> println("x == args[0]")//这个是说args[0]是否与x一样
    }
}

//when与if一样,也有返回值,都是每一个分支最后一行表达式的值;
    val mode = when {
        args.isNotEmpty()&& args[0] == "1" -> 1
        else -> 0
    }

表达式总结:

  • if ... else ...这个用法基本和Java中一致;
  • 表达式必须具备完整性,如果只有if没有else编译报错;
 var b = if (mode == 0) {
        0
    } 
    //如果没有下面的分支语句,编译报错
    //赋值时,分支必须完备
    else {
        1
    }
  • when表达式,加强版的switch,支持任意类型,也不用写break;
    支持纯表达式条件分支(类似if)
   val mode = when {
       args.isNotEmpty()&& args[0] == "1" -> 1
       else -> 0
   }

必须具备完备性;

九、具名参数,变长参数,默认参数

  • 具名参数:就是在给函数传入实参的时候,把形参也赋值上,也就是下面在调用sum函数时,在传入实参的时候,把arg1 和 arg2也附带上(arg1 = 1,arg2 = 2);

具名参数的顺序可以不是固定的;

class ForDemo {

    fun sum(arg1:Int,arg2:Int) = arg1 + arg2
}

fun main(args: Array) {


    var forDemo = ForDemo()
    /**
     * 在使用函数传入实参的时候,把形参也赋上;
     * 因为是具名参数,所以形参的顺序可以不固定
     * 也就是明确告诉编译器这个2是给arg2的,1是给arg1的
     */
    forDemo.sum(arg2 = 2, arg1 = 1)

}
  • 变长参数
    某个参数可以接收多个值;
    变长参数可以不为最后一个参数;但是传入实参时,可以放在最后用具名参数表示即可;
    如果传参有歧义,需要使用具名参数指定某个特定参数;
    最常见的就是main函数,它里面是个数组,我们可以将它改变为变长参数;它的使用和数组完全一样
   var array = intArrayOf(1,23,43,25,56)

    hello(3.0,1,123,321,2,string = "hello")
    //*:Spread Operator这个符号现在只支持变长参数,且只支持array,不支持list
    hello(3.0,*array,string = "hello")

fun hello(double:Double,vararg ints: Int,string:String) {
    ints.forEach { ::println }
}

在Java中变长参数只能是函数的最后一个参数,否则编译器无法识别的;
在kotlin中因为存在具名函数,因此它可以放在任意位置;在传入实参的时候可以用具名函数放在最后一个位置;

*:Spread Operator 这个不是一般的运算符;
只支持展开Array;
只用于变长参数列表的实参;
不能重载;

  • 默认参数
    1.就是在函数编写时,可以给函数任意位置的形参一个默认值;在实际调用函数时,这个参数可以不传;
    2.但是如果该默认参数不是最后一个,其他参数使用具名参数即可,不用写默认的;
    3.传参出现歧义时,也就是编译器报错,需要使用具名函数,具体请看第二条;
    hello(3.0,1,123,321,2,string = "hello")
    //* 这个符号现在只支持变长参数,且只支持array,不支持list
    hello(ints = *array,string = "hello")

fun hello(double:Double = 3.0 ,vararg ints: Int,string:String) {
    ints.forEach { ::println }
}

你可能感兴趣的:(第一章 Kotlin基础语法)