Kotlin语法总结

变量

变量是一门编程语言最基础和重要的功能,kotlin的变量定义很有特色。

  1. 自动推导类型。C++,java等高级语言在声明变量的时候需要指明变量类型,如
    int value = 0;。但是kotlin拥有出色的类型推导机制,在变量声明并初始化的时候,可以根据初始化值的不同,自动推导出变量类型,如
//整型变量
val num1 = 0
//浮点数
val num2 = 0.0
//字符串
val str = "Hello world!"
  1. 显式声明变量类型。当变量声明但不初始化的时候,编译器无法自动推导出变量类型,这时候需要我们进行显式的声明变量类型,如
val num:Int
  1. var和val。如前所述,kotlin在声明变量时无需指明变量类型,而是由编译器自动推导,但是在变量声明前添加两种关键字,即valvar
  • val (value)的简写,表明这是一个常量,必须初始化,且初始化以后不能再改变。对应java的final。
  • var(variable)的简写,表明这是一个变量。
    技巧:尽量使用val。大型项目的开发需要多人协作,我们通常不希望自己的变量被别人随便修改,因为这很容易出现一些难以排查的bug。因此,我们要尽可能使用val,只有当val无法满足需求时,才使用var
  1. 纯面向对象。java中的变量类型分为基本数据类型和对象数据类型,而kotlin摒弃了java中的基本数据类型,全部都用对象实现。int是java中的关键字,而Int(注意到这里首字母大写)是kotlin中的一个类,拥有自己的继承结构与类方法。类似的,Double,Float,Long等也都是类。

函数

任何高级语言都实现了函数,这大大提高了代码复用率,并使逻辑清晰,减少了程序员犯错的可能性。

  1. 函数定义的基本格式
fun function(paraml1:Int, paraml2:Int):Int
{
	return 0
}
  1. 当函数只有一行代码时,不必编写函数体,如实现求两个数中较大的那个函数定义如下。
fun largeNumber(num1:Int, num2:Int) = max(num1, num2)
  1. 函数的默认参数值。给函数参数设置默认参数值,则不必要求该函数被调用时,必须为此变量赋值,而使用默认的参数值。不同于java的是,kotlin不必规定默认参数必须放在所有参数的最右边。这是因为kotlin不必按照定义的顺序传参。请看下面的传参方式。
    var num = largerNumber(num1 = 1, num2 = 2)

if条件语句

  1. kotlit的if语句基础功能与绝大多数高级语言一样,如下面的函数的功能返回两个数中较大的那个。
fun largerNumber(num1:Int, num2:Int):Int
{
    var value = 0
    if(num1 > num2)
    {
        value = num1
    }
    else
    {
        value = num2
    }
    return value
}
  1. kotlin的if语句可以有返回值,其返回值就是每一个条件分支中的最后一行代码,因此上面的代码可以改写成如下形式。
fun largerNumber(num1:Int, num2:Int):Int
{
    return if(num1 > num2)
    {
        num1
    }
    else
    {
        num2
    }
}

when语句

  1. when语句类似于java中的switch。当条件分支较多时,用when语句可以让代码显得很简洁,如实现一个用阿拉伯数组转换成汉字显示的星期几如下。
fun convert(x:Int):String
{
    var result = ""
    when(x)
    {
        1 -> result = "星期一"
        2 -> result = "星期二"
        3 -> result = "星期三"
        4 -> result = "星期四"
        5 -> result = "星期五"
        6 -> result = "星期六"
        7 -> result = "星期日"
        else -> result = "非法输入"

    }
    return result
}
  1. when语句与switch的不同之处如下:
  • 无需在每个分支后面增加break语句。java中的switch语句中每个case后面都要手动添加break语句,否则执行完当前case之后,就会顺序执行完之后所有的case,而kotlin则只执行当面语句。
  • when语句允许传入任意类型的参数。java中的switch语句中的判断类型只能是整型或者短整型。
  • when语句支持类型匹配。请看下面的例子。这里Number是Kotlin内置的一个抽象类,IntLongDoubleFLoat等都是其子类。而is关键字用来判断数据类型,相当于java的instanceof关键字。
fun whenTest(x:Number)
{
    when(x)
    {
        is Int -> println("$x is Int")
        is Double -> println("$x is Double")
        else -> println("无法识别")
    }
}
  • when扩展用法:不带参数。当when不带参数时,将完整的判断表达式写在分支当中。请看下面的例子。
fun pieseWiseFun(x:Double):Int
{
    var result = 0
    when
    {
        x < 0 -> result = 0
        x < 1 -> result = 1
        else -> result = 2
    }
    return result
}

循环语句

kotlin循环语句主要是when循环和for循环,when循环基本和java一样。

for循环

  1. 区间
  • 双端闭区间val range = 0..5。等价于0,1,2,3,4,5
  • 左闭右开区间val range = 0 until 5
  • 步长设置val range = 0 until 5 step 2等价于0,2,4
  • 降序区间5 downTo 1等价于5,4,3,2,1
  1. for in 循环:对区间,集合,数组等遍历。
    for(i in 1 until 10)
    {
        println("i = $i")
    }

面向对象编程

构造函数

构造函数是在生成对象时调用,其作用主要是完成类内字段的初始化。kotlin的构造函数比较复杂,分为主构造(没有函数体)和次构造(有函数体)。

  1. 主构造函数:没有函数体。每个类只能有一个主构造函数,下面的例子定义了一个Person类,其数据成员是nameage
    注意
  • 这里的构造函数的定义和java不太一样,直接和类的定义写在了一起,而不是单独定义了一个函数。
  • 在主构造函数中声明为valvar的参数,将自动变成该类的数据成员。
open class Person(val name:String, val age:Int) {
    fun eat(){
        println(name + " is eating, He is " + age + " years old.");
    }
}
  1. 主构造函数中写函数体的方法。如果我们想在主构造函数中实现一些逻辑,可以采用kotlin提供的init结构体,如下所示。可以看到,这里我们只是将其数据成员打印而已。
open class Person(val name:String, val age:Int) {
    init {
        println("name = $name")
        println("age = $age")
    }
    fun eat(){
        println(name + " is eating, He is " + age + " years old.");
    }
}
  1. 次构造函数:可以有函数体,用关键字constructor修饰。当一个类既有主构造函数,又有次构造函数时,次构造必须调用主构造,如下所示。这里的次构造函数仅有一个输入参数,通过调用主构造函数完成初始化。
    这里的次构造函数提供了一个只需一个name参数的构造方法。
open class Person(val name:String, val age:Int) {
    init {
        println("name = $name")
        println("age = $age")
    }
    constructor(name: String):this(name, 0)
    {
    }
    fun eat(){
        println(name + " is eating, He is " + age + " years old.");
    }
}

注意:当在类中不显式的定义主构造函数而定义了此构造函数时,这个类就没有主构造函数,只有次构造函数。此时,次构造函数需要调用其父类的构造函数。这种情况很少发生。

类继承

继承是面向对象的重要特性之一,其特点是在现有类的基础上拓展端口生成新的类。如此便可在降低程序员工作量的同时提高代码的可靠性。

  1. kotlin默认类是不可继承的。众所周知,java中有关键字final,其主要作用是声明该类时终结类,不能被别的类继承。kotlin中任何一个非抽象类默认情况下都是不可继承的,相当于被声明为java中的final。之所以这么设计,是因为如果一个类允许被继承,那么它无法预知他的子类如何实现,可能出现一些意想不到的bug。因此除非这个类是专门为继承而设计的,否则都应该定义为不可被继承的。
  2. 关键字:open。在类定义时,用关键字open修饰,可以将类声明为可以被继承的。请看一个继承的例子。
//基类:请注意这里的关键字open
open class Person{
    var name = ""
    var age = 0
}

//子类
class Student(val grade:Int, name: String, age:Int):Person(){
}

几点说明

  • 这里的Person类被关键字open修饰,意在告诉编辑器,Person类可以被继承。
  • java中的继承采用关键字extends,而kotlin中用:
  • Person类中没有显式的定义构造函数,但是编译器自动生成了一个不用任何参数的构造函数,即默认构造函数。
  • kotlin中的继承的特殊之处在于,Person后面有一对括号。这是因为子类的构造函数必须调用基类的构造函数,这就相当于在Student的主构造函数中调用Person类中的默认构造函数。
  • Student类中,主构造函数的参数有3个。其中grade被val修饰,编译器自动将被valvar修饰的变量设置为本类的字段。而nameage则没有被修饰,因为他们是为其基类准备的参数。

lambda编程

lambda是一段可以作为参数传递的代码。正常情况下,调用函数时只能传入变量,而借助lambda,我们可以传入代码。传入的这段代码不宜过长,否则影响可读性。

  1. lambda的语法结构。运算符->是参数和函数体的分隔符,函数体中的最有一行是lambda表达式的返回值。
{参数1:参数类型,参数2:参数类型 -> 函数体}

lambda最常见的用法是和集合一起使用,我们先看看集合的定义。
2. 不可变集合:必须初始化,且初始化之后就变成只读的,不能修改,增加,删除元素。用函数listOf()定义。

    val list = listOf("Apple", "Banana", "Orange", "Pear")

3.可变集合:用函数mutableListOf()函数定义。

    val list2 = mutableListOf("Red", "Blue", "Green")

集合的函数式API

考虑这样的需求:如何寻找上述集合中长度最长的一个元素,这里可以借助lambda表达式完成。函数maxBy()的功能是根据穿进去的参数,选择集合中该参数最大的那个。
注意到这里maxBy()的参数是一个lambda表达式:
{fruit:String -> fruit.length}

    val maxLengthFruit = list1.maxBy({fruit:String -> fruit.length})

上述表达式可以进行简化,简化规则如下

  • 当lambda表达式是函数的最后一个参数时,可以将lambda表达式写在函数的小括号后面。
    val maxLengthFruit = list1.maxBy(){fruit:String -> fruit.length}
  • 当lambda表达式是函数的唯一参数时,可以省略小括号。
    val maxLengthFruit = list1.maxBy{fruit:String -> fruit.length}

当lambda表达式中只有一个参数时,不必进行参数声明,并用it代替。

    val maxLengthFruit = list1.maxBy{it.length}

java函数式API的使用

  1. 如果在kotlin中调用了一个java方法,并且该方法知接收一个单抽象方法接口参数,就可以使用函数式API。所谓单抽象方法接口是指接口中只有一个待实现的方法。
    java中有一个常见的单抽象方法接口——Runnable(),此方法中只有一个待实现的方法,如下所示:
public interface Runnable(){
	void run()
}

Runnable常常结合线程一起使用,Thread类的构造方法中接收一个Runnable接口,并创建一个子线程,如下所示。

new Thread(new Runnable(){
	@Override
	public void run()
	{
		printLn("This is a Thread")
	}
}).start

这里使用了匿名类的写法,将其翻译成kotlin就是

    Thread(object : Runnable{
        override fun run() {
            println("This is a Thread")
        }
    }).start()

注意到这里符合使用lambda的条件,因此我们改写之。

    Thread(Runnable { 
        println("This is a Thread")
    }).start()

因为这里Runnable只有一个待实现的函数,因此即使我们不写函数名,也不会产生歧义。
但这里还不是最简形式,当我们该方法的形参表中仅有一个单抽象方法接口时,我们还可以省略接口名,如下所示

    Thread({ 
        println("This is a Thread")
    }).start()

当lambda是最后一个表达式时,还可以将lambda表达式写在小括号外面。
当lambda表达式是唯一的参数时,还可以将小括号省略,因此最终的简化形式如下

    Thread{ 
        println("This is a Thread")
    }.start()

空指针检查

空指针异常是常见的bug之一。为了解决这个问题,kotlin有一系列的措施。

  1. 可空数据类型:我们之前所见的数据类型,如Int,String等都是不可空的。在其后面加上问号,即Int?,String?就是可空数据类型。
  2. 判空运算符 ?.:其调用形式同“.”,即对象名?.方法()。其作用是当对象非空时,正常调用。当对象是空时,什么也不做。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值