IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin笔记 -> 正文阅读

[移动开发]Kotlin笔记

笔记源于《第一行代码》-郭霖

2.3 变量和函数

2.3.1 变量

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

val用来声明一个不可变的变量,这种变量在初始赋值后就再也不能重新赋值,对应Java中的final变量
var用来声明一个可变的变量,这种变量在初始赋值后可以重新赋值,对应Java中的非final变量

要注意的是,在Kotlin中每行代码的结尾是不需要加分号的

为什么只有两种变量类型?
因为Kotlin有类型推导机制。

fun main(){
    val a = 10 //a自动推导成整型变量
    println("a = " + a)
}

但是Kotlin的类型推导机制并不是总可以正常工作的,如果我们对一个变量延迟赋值的话,Kotlin就无法自动推导它的类型了。这时候就需要显式地声明变量类型才行。

val a: Int = 10

既然val关键字有这些束缚,为何还要使用这个关键字?
Kotlin这样的设计是为了解决Java中final关键字没有被合理利用的问题。
在Java中,除非主动在变量前声明final关键字,否则该变量就是可变的。但当项目愈发复杂的时候,一个可变的变量很有可能不知道就被谁修改了,就会出现难以想象的问题,因此,一个好的习惯就是,除非一个变量明确允许被修改,否则都应该加上final关键字,但是许多人都没有这样的习惯,所以Kotlin在设计时就提供了val和var两个关键字,必须由开发者自行选择该变量是否可变。
在开发时应该优先使用val,当val无法满足使用时再使用var。

Kotlin的数据类型的名称与Java的有所区别,Kotlin中的数据类型的名称的首字母是大写的,而Java中则全为小写。不要小看两者大小写之间的差距,这表示Kotlin完全抛弃了Java中的基本数据类型,全部使用了对象数据类型。在Java中int是关键字,而在Kotlin中Int变成了一个类,它拥有自己的方法和继承结构。

KotlinJava
Intint
Longlong
Shortshort
Floatfloat
Doubledouble
Charchar
Booleanboolean
Bytebyte

2.3.2 函数

Kotlin的函数语法规则大致如下:

fun methodName(param1: Int, param2: Int): Int{ //fun是定义函数的关键字
    return 0
}

参数的声明格式是“参数名:参数类型",如果不想接收任何参数,就只写一对空括号即可。
参数括号后面的那部分是可选的,用于声明该函数会返回什么类型的数据,如果函数不需要返回任何数据,这部分可以不写。

mian函数是kotlin程序的入口函数。
例:

fun main(){
    val a = 35
    val b = 45
    val value = largerNumber(a, b)
    println("larger number is " + value)
}
/*
定义了一个名为largerNumber的函数,该函数接收两个整型参数,
并返回两个参数中更大的一个
*/
fun largerNumber(num1: Int, num2: Int): Int{
    return max(num1, num2)
}

当一个函数中只有一行代码时,Kotlin允许不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可,比如上方的largerNumber()函数可以简化:

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

使用这种语法,return关键字也可以省略,等号足以表达返回值的意思,另外,上方还提到了kotlin的类型推导机制,在此也可以发挥作用。由于max()函数返回的是一个Int值,且我们在largerNumber()函数的尾部使用了等号连接max()函数,因此Kotlin可以推导出largerNumber()函数返回的必然是一个Int值,这样就不用显式地声明返回值类型了。

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

2.4 程序的逻辑控制

2.4.1 if条件语句

Kotlin中的条件语句主要有两种实现方式:if和when
可以将上方的largerNumber()改写:

fun largerNumber(num1: Int, num2: Int): Int{
    var value = 0
    if(num1 > num2){
        value = num1
    }else{
        value = num2
    }
    return value
}

Kotlin中的if语句比Java的多了一个额外功能,它可以有返回值,返回值就是if语句中每一个条件中最后一行代码的返回值。因此,上述代码就可以简化:

/*
if语句使用每个条件的最后一行代码作为返回值,
并将返回值赋值给了value变量
*/
fun largerNumber(num1: Int, num2: Int): Int{
    val value = if(num1 > num2){
        num1
    }else{
        num2
    }
    return value
}

但是上述代码可以再精简,value变量也可以删去

fun largerNumber(num1: Int, num2: Int):Int{
    return if(num1 > num2){
        num1
    }else{
        num2
    }
}

注意,在Kotlin中当一个函数只有一行代码时,可以省略函数体部分,直接将这一行代码使用等号串联在函数定义的尾部。虽然上述函数代码不只一行,但是它和只有一行代码的作用是相同的,只是返回了if的返回值,符合语法糖的使用条件。

fun largerNumber(num1: Int, num2: Int) = if(num1 > num2){
    num1
}else{
    num2
}
//更简化版
fun largerNumber(num1: Int, num2: Int) = if(num1 > num2) num1 else num2

2.4.2 when条件语句

Kotlin中的when语句有点类似与Java中的switch语句,但它的功能较多。

//if功能实现getScore()函数
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
}
//when功能实现getScore()函数
fun getScore(name: String) = when(name){
    "Tom" -> 86
    "Jim" -> 77
    "Jack" -> 95
    "Lily" -> 100
    else -> 0
}

when语句和if语句一样,也是可以有返回值的。
when语句允许传入一个任意类型的参数,然后可以在when的结构体中定义一系列的条件,格式是:

匹配值 -> {执行逻辑}

当执行逻辑只有一行代码时,{}可以省略。
除了精确匹配外,when语句还允许进行类型匹配。

/*
这里的is关键字相当于Java中的instanceof关键字
由于checkNumber()函数接受一个Number类型的参数
这是Kotlin内置的一个抽象类
想Int,Long,Float等与数字相关的类都是他的子类
*/
fun checkNumber(num: Number){
    when(num){
        is Int -> println("number is Int")
        is Double -> println("number is Double")
        else -> println("number not support")
    }
}

when语句还有一种不带参数的用法

/*
这种用法是将判断的表达式完整地写在when的结构体中
Kotlin中判断字符串或对象是否相等可以直接使用==关键字
*/
fun getScore(name: String) = when{
    name == "Tom" -> 86
    name == "jim" -> 77
    name == "Jack" -> 95
    name == "Lily" -> 100
    else -> 0
}
/*
假设所有名字以Tom开头的人,分数都为86分,使用不带参数的when语句
*/
fun getScore(name: String) = when{
    name.startsWith("Tom") -> 86
    name == "jim" -> 77
    name == "Jack" -> 95
    name == "Lily" -> 100
    else -> 0
}

2.4.3 循环语句

Kotlin也提供了while循环和for循环,其中while循环不管是语法还是使用技巧上都和Java中的while循环没有任何区别。
我认为Kotlin中的for循环类似于Python中的for循环。
用Kotlin代码表示一个区间:

/*
上述代码创建了一个0到10的区间,并且两端都是闭区间,
这意味着0到10这两个端点都是包含在区间中的
其中,..是创建两端闭区间的关键字。
*/
val range = 0..10

有了区间后,就可以通过for-in循环来遍历这个区间

fun main(){
    for(i in 0..10){
        println(i)
    }
}

但是在很多情况下,双端闭区间却不如单端闭区间好用,许多数组的下标都是从0开始,一个长度为10的数组,下标范围为0到9,因此左闭右开的区间在程序设计中更常用。Kotlin中可以使用until关键字来创建一个左闭右开的区间

val range = 0 until 10

Kotlin中也有步长step关键字

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

创建降序的区间,可以使用downTo关键字

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

2.5 面向对象编程

Kotlin是面向对象的

2.5.1 类与对象

Kotlin中也是使用class关键字来声明一个类

class Person{
    var name = ""
    var age = 0

    fun eat(){
        println(name + " is eating. He is " + age + " years old.")
    }
}

Kotlin中如何对类进行实例化?

/*
Kotlin中实例化一个类的方式和Java是基本类似的,
只是去掉了new关键字而已
*/
val p = Person()

可以在main()函数中对p对象进行一些操作

fun main(){
    val p = Person()
    p.name = "Jack"
    p.age = 19
    p.eat()
}

2.5.2 继承与构造函数

要是子类继承父类,第一件事,使父类可以被继承。可能有人会觉得奇怪,一个类本身不就是可以被继承的吗?为什么还要父类可以继承?这就是Kotlin不同的地方,在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于在Java中给类声明了final关键字。之所以这么设计,其实和val关键字的原因差不多,因为类和变量一样,最好都是不可变的,而一个类允许被继承的话,它无法预知子类会如何实现。
所以Kotlin默认所有非抽象类都是不可被继承的。要使父类可以被继承,只需要在父类前面加上open关键字就可以了:

/*
加上open关键字后,就是在主动告诉Kotlin编译器,
Person这个类是专门为继承而设计的,这样Person类就允许被继承了
*/
open class Person(){
    ...
}

第二件事,要让子类继承父类,在Java中继承的关键字是extends,而在Kotlin中变成一个冒号:

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

继承的写法如果只是替换一下关键字倒也简单,但是为什么Person类的后面要加上一对括号呢?因为这里还设计主构造函数、次构造函数等方面的知识。
任何一个面向对象的编程语言都会有构造函数的概念,Kotlin中也有,但是Kotlin将构造函数分为两种:主构造函数和次构造函数。
主构造函数将会是你最常用的构造函数,每个类默认都会有一个不带参数的主构造函数当然也可以显式地指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可:

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

这里将学号和年级两个字段都放在主构造函数中,就表明在对Student类进行实例化的时候,必须传入构造函数中要求的所有参数:

/*
这样我们就创建了一个Student对象,
同时学号为a123,年级是5
由于构造函数中的参数是创建实例时传入的,不像之前的写法那样要重新赋值,
因此可以将参数全部声明为val
*/
val student = Student("a123", 5)

那如果主构造函数没有函数体,想在主构造函数中编写一些逻辑该怎么办?
Kotlin提供了一个init机构提,所有主构造函数中的逻辑都可以写在里面:

class Student(val sno: String, val grade: Int) : Person(){
    init{
        println("sno is " + sno)
        println("grade is " + grade)
    }
}

那这些跟那对括号有什么关系呢?这就涉及到Java继承特性中的一个规定,子类中的构造函数必须调用父类中的构造函数,在Kotlin中也要遵守。
回头看一下Student类,现在我们声明了一个主构造函数,根据继承特性的规定,子类的构造函数必须调用父类的构造函数,可是主构造函数并没有函数体,该如何去调用父类的构造函数?你可能会说,在init结构体中调用不就好了?这或许是一种办法,但绝对不是一种好办法,因为在绝大多数的场景下,我们是不需要编写init结构体的。
Kotlin采用了一种简单但是不太好理解的设计方式:括号。子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定。所以回头再看一遍这段函数:

/*
Person类后面的一对空括号表示
Student类的主构造函数在初始化的时候会调用Person类的无参数构造函数,
即使在无参数的情况下,这对括号也不能省略。
*/
class Student(val sno: String, val grade: Int): Person(){}

如果我们将Person改造一下:

/*
此时Student类一定会报错,错误的原因很明显,
Person类后面的空括号表示要去调用Person类中无参的构造函数,
但是Person类现在已经没有无参的构造函数了。
*/
open class Person(val name: String, val age: Int){...}

如果想解决这错误,就必须给Person类的构造函数传入name和age字段 ,可是Student类也没有这两个字段。没有的话那就加上,我们可以在Student类的主构造函数中加上name和age两个参数,再将这两个参数传给Person类的构造函数:

/*
注意,在Student类的主构造函数中增加name和age这两个字段时,
不能再将它们声明成val,
因为在主构造函数中声明成val或者var的参数将自动成为该类的字段,
这就会导致和父类中同名的name和age字段造成冲突。
因此,这里的name和age参数前面不用加任何关键字,
让它的作用域仅限定在主构造函数中即可。
*/
class Student(val sno: String, val grade: Int, name: String, age: Int): Person(name, age){
    ...
}

那什么是次构造函数呢?任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数可以用于实例化一个类,这一点和主构造函数没什么不同,只不过它是有函数体的。
Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有次构造函数都必须调用主构造函数(包括间接调用)。
例:

/*
次构造函数是通过constructor关键字来定义的,
这里我们定义了两个次构造函数:
第一个次构造函数接收name和age参数,然后它又通过this关键字调用了主构造函数,
并将sno和grade这两个参数赋值成初始值;
第二个次构造函数不接受任何参数,
它通过this关键字调用了刚才定义的第一个次构造函数,
并将name和age参数也赋值成初始值,由于第二个次构造函数间接调用了主构造函数,
因此这仍然是合法的。
*/
class Student(val sno: String, val grade: Int, name: String, age: Int): Person(name, age){
    constructor(name: String, age: Int): this("", 0, name, age){}
    constructot(): this("",0){}
}

还有一种非常特殊的情况:类中只有次构造函数,没有主构造函数。这种情况真的十分少见,但在Kotlin中是允许的。当一个类没有显式地定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。

/*
注意此处Student类的后面没有显式地定义主构造函数,
同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。
既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。
另外,由于没有主构造函数,次构造函数只能直接调用父类的构造函数,
此段代码也是将this关键字换成了super关键字。
*/
class Student: Person{
    constructor(name: String, age: Int): super(name, age){}
}

2.5.3 接口

接口是用于实现多态编程的重要组成部分。我们都知道,Java是单继承结构的语言,任何一个类最多只能继承一个父类,但是却可以实现任意多个接口,Kotlin也是如此。我们可以在接口中定义一系列的抽象行为,然后由具体的类去实现。

interface Study{
    fun readBooks()
    fun doHomework()
}
/*
Java中继承使用的关键字是extends,实现接口使用的关键字是implements,
而Kotlin中统一使用冒号,中间用逗号进行分隔。
此段代码就表示Student类继承了Person类,同时还实现了Study接口。
另外接口后面不用加上括号,因为他没有构造函数可以去调用。
Kotlin中使用override关键字来重写父类或者实现接口中的函数。
*/
class Student(name: String, age: Int): Person(name, age), Study{
    override fun readBooks(){
        println(name + " is reading.")
    }
    override fun doHomework(){
        println(name + " is doing homework.")
    }
}

我们可以在main()函数中编写如下代码来调用两个接口中的函数:

fun main(){
    val student = Student("Jack", 19)
    doStudy(student)
}
fun doStudy(study: Study){
    study.readBooks()
    study.doHomework()
}

Kotlin还有一个额外的功能:允许对接口中定义的函数进行默认实现。其实Java在JDK1.8后也开始支持这个功能了,因此总体来说,Kotlin和Java在接口方面的功能仍然是一模一样的。

/*
我们在doHomework()函数加上了函数体,并且在里面打印了一行日志。
如果接口中的一个函数拥有了函数体,这个函数体中的内容就是它的默认实现。
现在当一个类去实现Study接口时,只会强制要求实现readBooks()函数,
而doHomework()函数则可以自由选择实现或者不实现,
不实现时就会自动使用默认的实现逻辑。
*/
interface Study{
    fun readBooks()

    fun doHomework(){
        println("do homework default implementation.")
    }
}

接下来我们在来看一下Kotlin和Java相比变化较大的部分–函数的可见性修饰符。
熟悉Java的人都知道,Java中有public, private, protected, default(什么都不写)这4种函数可见性修饰符。Kotlin中也有4中,分别是public, private, protected, internal,需要使用哪种修饰符时,直接定义在fun关键字的前面即可。

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

2.5.4 数据类与单例类

在一个规范的系统架构中,数据类通常占据着非常重要的角色,它们用于将服务器端或者数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。
Kotlin中只需要在一个类前面声明了data关键字,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals(), hashCode(), toString()等固定且无实际逻辑意义的方法自动生成,从而大大减少开发的工作量。

data class Cellphone(val brand: String, val price: Double)
//测试数据类
fun main(){
    val cellphone1 = Cellphone("Samsung", 1299.99)
    val cellphone2 = Cellphone("Samsung", 1299.99)
    println(cellphone1)
    println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}

在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。

object Singleton{}

现在Singleton就已经是一个单例类了,我们可以直接在这个类中编写需要的函数

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

可以看到,在Kotlin中我们不需要私有化构造函数,也不需要提供getInstance()这样的静态方法,只需要把class关键字改成object关键字,一个单例类就创建完成了,而调用单例类中的函数也很简单,比较类似于Java中静态方法的调用方式:

Singleton.singletonTest()

这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-28 12:02:03  更:2022-01-28 12:02:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:52:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码