Kotlin基础学习

写在前面

在刚开学的时候,买了一本《第一行代码Android》,但一直在上课没有机会看,这几天刚好写完了上一个项目,这段时间就对这本书进行了学习。在这本书中,由于谷歌大力推广kotlin语言,将其作为android开发的首推语言,本书也遵循了谷歌的推广,因此我就跟着书本学习了基本的Kotlin语法,当作记录笔记了。

变量和函数

变量

在Koltin中的变量定义方式与java有很大不同,在kotlin中要定义一个变量,只允许在变量前声明两种关键字:val和var,val是value的简写,表示一个不可变的变量,对应java中的final关键字定义的变量。var是variable的简写,表示一个可变的变量,对应java中的非final关键字定义的变量。这时可能学过java的都会冒出一个疑惑,只靠这两个怎么能知道具体的类型呢?Kotlin里有类型推导机制,如:

  val a = 37
  println("a = " + a)

这里可以看出,Kotlin不需要行尾分号了,这里我们将10赋值给了a,那么a就会被推导成整型变量。如果你把一个字符串赋值给a变量,那么a就会被自动推导为字符串变量。但需要注意的是,类型推导机制并不总是好用的,因此我们可以对一个变量进行显式声明,如:

val a:Int = 10

Kotlin里摒弃了java中的int,double,float等,全部都改用了对象数据类型,即首字母大写的类型,如Int,Double,Float。此时我们尝试对a变量进行一个扩大的操作:

a = a * 10

可以看到编译器报错了,因为val类型的变量无法被重新赋值,我们把a的类型变成var就可以了:

var a : Int = 10

函数

首先要注意的是,方法和函数其实说的都是一个东西,只是叫法不同而已。接下来我们看看如何在kotlin中定义一个函数:

fun largerNumber(num1:Int,num2:Int):Int{
    return max(num1,num2)
}

fun表示function,即声明函数,后面就是函数名了。函数名后的括号里为变量参数列表,参数的声明格式为:"参数名:参数类型"。参数后面的冒号内容可以省略,表示返回值类型,这里我们声明为返回一个Int型的数据。这里使用了一个max函数,表示取二者的最大值。

这里我们提一句语法糖,当返回值只有一句代码时候,我们可以将大括号省略,用等号连接,且return也可以省略。如下:

fun largerNumber(num1:Int,num2:Int):Int = max(num1,num2)

而我们刚才提到了kotlin的类型推导机制,返回值肯定是一个Int值,因此我们不需要显式声明返回值类型了。就可以写成:

fun largerNumber(num1:Int,num2:Int) = max(num1,num2)

逻辑控制

if条件语句

Kotlin的if和java几乎没有任何区别,但需要注意的是,kotlin中的if条件语句是有返回值的,返回值为if语句每个条件中的最后一行代码的返回值。意味着我们可以这么写:

fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

这里又使用了上面提到的语法糖,因为这里放到大括号里也就是return 一句话,所以符合语法糖的规则。

when条件语句

kotlin中的when有点像java中的switch,但比起switch要强大的多。switch里面只能传入整型和短于整型的变量作为条件,JDK1.7后还引入了字符串变量的支持。但如果我们传入的类型不是这些,那么switch语句就无法使用了。同时,switch语句的最后需要加一个break,否则会顺序执行每个case。而kotlin不仅解决了以上的痛点,还添加了很多好用的功能。

比如我们先看一个简单的通过姓名输出学生分数的功能:

fun getScore(name:String) = if(name =="Tom"){
    86
}else if(name == "Jim"){
    77
}else if(name == "Jack"){
    95
}else if(name == "Lily"){
    100
}else{
    0
}

这里又再次使用了单行代码函数的语法糖,但写了这么多if和else显得代码十分冗余,这里我们改用when的写法:

fun getScore(name: String) = when (name) {
    "Tom" -> 86
    "Jim" -> 77
    "Jack" -> 95
    "Lily" -> 100
    else -> 0
}

when语句可以传入一个任意类型的变量,然后在when的结构体定义一系列的条件,格式为: 匹配值 -> {执行逻辑},当执行逻辑只有一行的时候,大括号就可以省略了。

除了精确匹配外,when还支持类型匹配,比如我们定义一个checkNumber()函数:

// when类型匹配
fun checkNumber(num: Number) {
    when (num) {
        is Int -> println("number is Int")
        is Double -> println("number is Double")
        else -> println("number not support")
    }
}

在上面的代码中,is相当于java中的instance of,而Number是kotlin中的一个抽象类,像Int,Float,Double等与数字有关的类都是他的子类,我们可以通过类型匹配来判断是什么类型。比如:

val num = 10.0
checkNumber(num)

就会输出Double类型了。

需要注意的是,kotlin中判断字符串或者对象是否相等用 == 就可以了,不再需要调用equals方法。

循环语句

kotlin中有两种循环,while和for循环,而while循环和java基本一模一样,因此不再提了,而java中常用的for-i循环被kotlin舍弃,java中的另一种for-each循环则被大幅加强,变成了for-in循环。在学习循环前,要先学习区间的概念,这也是java种没有的。如下代码:

val range = 0..10

这段代码的意思其实就是[0,10]的意思,有了区间,我们就可以用for-in来遍历区间了:

 for (i in range){
     println(i)
}

但很多时候,我们一般不会用左右闭区间,而会使用左闭右开区间,kotlin也提供了until关键字来实现这个功能:

  for (i in 0 until 10){
    println(i)
  }

这时生成的就是[0,10)区间了。

默认情况下,会在区间范围内递增1,我们可以用step来递增更多,如:

for(i in 0 until 10 step 2){
    println(i)
}

这时就是递增2了,相当于i+=2的意思。

但需要注意的是,..和until都只能左边小右边大,也就是只能创建升序区间,而创建降序区间可以用downTo,如:

for (i in 10 downTo 1){
	println(i)
}

downTo也可以使用step,不再多提了。

类与对象

基本用法

在面向对象语言中,类和对象是很重要的概念。kotlin自然也有这样的概念。

我们先来通过IDE来定义一个类:

class Person() {
}

可以看到,kotlin也是使用class关键字来声明类的,这点与java一致。我们定义几个变量和函数:

class Person() {
    var name = ""
    var age = 0
    fun eat(){
        println(name + " is eating.He is " + age + " years old")
    }
}

这里用var是因为我们要在创建对象后对其进行重新赋值,eat函数就不再解释了,很简单。

接下来我们把这个类实例化一下:

val p = Person()
p.name = "Jack"
p.age = 19
p.eat()

可以看到,与java不同的是,我们不再需要new关键字了,因为你调用一个类的构造方法只可能是为了对类进行实例化。接下来我们执行这段代码,发现结果和我们想的一致。

继承与构造函数

面向对象的一个重要特性就是继承,我们先定义一个Student类:

class Student {
}

要让Student类继承Person类,我们要干两件事:

  1. 使Person类可以被继承。这话听起来似乎很怪,java中所有类默认都是可以被继承的,但Kotlin却不是,在Kotlin中任何一个非抽象类默认都是不可以被继承的,而抽象类如果不继承就无任何意义,所以必须被继承。想要让一个类可以被继承,其实很简单,加上一个open关键字就可以了:

    open class Person() {
        var name = ""
        var age = 0
        fun eat(){
            println(name + " is eating.He is " + age + " years old")
        }
    }
    
  2. 第二件事,自然就是让Student类继承Person类了,如下写法:

    class Student:Person(){
        var sno = ""
        var grade = 0
    }
    

    继承的写法首先,不再需要extends关键字,加上冒号即可。之后如果仔细观察,会发现多了一对括号。而要理解括号,就要解释另一个概念——构造函数了。

在Kotlin中,有两类构造函数,主构造函数与次构造函数,主构造函数只能有一个,且没有函数体。直接定义在类名的后面。而次构造函数则是可以有任意多个,且有函数体。如下例子:

class Student(var sno:String,var grade:Int) 

这种就是典型的主构造函数定义了。如果我们要在主构造函数里写一些逻辑,就需要用到init结构体了:

init{
    println("sno is " + name)
    println("grade is " + age)
}

而根据继承的特性,子类的构造函数必须调用父类的构造函数。这时我们要怎么调用父类的主构造函数呢?自然是在继承上写了:

因此,如果我们再看上面的最开始的定义就会更加明了了。其实就是调用了父类的无参构造方法,就算是无参的也不能省略括号。如果我们对Person函数改造一下,将Person函数改成有参的构造方法:

open class Person(val name:String,val age:Int) {
}

此时的Student类要继承Person类,就必须实现他的主构造函数,即必须传入name和age两个值。我们的Student类就可以写成这样:

class Student(val sno:String,val grade :Int, name: String , age :Int):Person(name,age){
}

这里定义name和age时不能再声明成val,因为在主构造函数中声明的参数都会默认变成该类的字段。这会导致与同名的name和age发生冲突。因此不用再加任何关键字。

至于次构造函数,当一个类有主构造函数和次构造函数时,所有的次构造函数必须调用主构造函数。具体的例子就不再写了。

接下来我们看一个特殊情况,当一个类没有显式定义主构造函数而且定义了次构造函数时,它就是没有主构造函数的。此时继承Person类就不再需要加上括号了,因为没有主构造函数。

接口

kotlin是继承自java的语言,自然很多方面都很像了。在接口方面,与java有很多相似。我们先定义一个接口:

interface Study {
    fun readBooks()
    fun doHomework(){
        println("do homework default")

    }
}

这里kotlin和java一样,支持为接口定义默认函数体,使得实现类不需要实现这个方法。我们让Student类实现这个接口:

class Student( name:String, age:Int) :Person(name,age),Study {
    override fun readBooks() {
        println(name + "is reading")
    }
}

这里可以看到,kotlin中接口的关键字也是冒号,且只需要复写方法即可。我们在main函数调用该方法即可。

这里还需要提一下,函数的可见性修饰符。见下表:

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认)
private 当前类可见 当前类可见
protected 当前类、子类、同一包路径下的类可见 当前类、子类可见
default 同意包的路径下的类可见(默认)
internal 同一模块中的类可见

这里的模块概念暂时不深究。

数据类与单例类

在写web时候,model是很常用的一种类。我们在kotlin中如何定义一个数据类呢?如下:

data class CellPhone(val brand:String,val price:Double)

只需要加上data关键字,所有需要的方法就全部都实现了,如toString(),equals等等方法。

关于单例模式,也是我们很常见的一种设计模式。要使用单例类,如下:

object Singleton {
    fun singletonTest(){
        println("singletonTest is called")
    }
}

只需要定义为object类即可。要调用里面的方法,如下:

 Singleton.singletonTest()

即可。这样看上去是静态方法的调用,但其实kotlin在背后为我们自动创建了一个Singleton类的实例,并保证全局只会存在一个Singleton实例。

总结

这些只是最基本的知识,kotlin中更重要的特性例如Lambda编程,空指针检查机制等等就不再这里提了,以我的水平无法总结的很好。日后学习的多了之后再加深吧。

posted @ 2020-08-04 20:32  武神酱丶  阅读(761)  评论(0编辑  收藏  举报