Kotlin面向对象基础使用方法(继承、接口、Lambda、空指针检查机制等)

三、面向对象

1、继承

1.1 open改变类的继承属性

在kotlin设计时默认所有的非抽象类是无法被继承 的,如果想要使得一个非抽象类可以被继承,我们需要使用open关键字。

kotlin 复制代码
open class Person {
    var name ="";
    var age = 0;

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

同理如果类中的方法需要继承也需要使用open关键字

1.2 主构造函数与次构造函数

主构造函数:

  • 主构造函数的特点是没有函数体,直接定义在类名后面即可。
  • 主构造函数只能有一个,而且通常用于初始化类的属性,以及执行与类的整体初始化相关的操作。
  • 主构造函数的参数可以带有默认值,从而允许创建对象时省略某些参数。

我们使用init关键字编写主构造函数的逻辑

kotlin 复制代码
class Person(val name: String, val age: Int) {
    init {
        println("Person对象已创建,姓名:$name,年龄:$age")
    }
}

注意:

在主构造函数中声明val、var的字段会自动成为该类的字段,这样就会导致和父类中同名的字段冲突。如果不使用val、var关键字数据的作用域仅限定在主构造函数中。

副构造函数:

  • 副构造函数是类中的额外构造函数,可以有多个,它们用constructor关键字声明。
  • 副构造函数通常用于提供不同的构造方式,允许使用不同的参数组合来创建对象。
kotlin 复制代码
class Person(val name: String, val age: Int) {
    // 主构造函数
    init {
        println("Person对象已创建,姓名:$name,年龄:$age")
    }

    // 副构造函数1,接受姓名参数
    constructor(name: String) : this(name, 0) {
        println("副构造函数1被调用")
    }

    // 副构造函数2,接受年龄参数
    constructor(age: Int) : this("未知姓名", age) {
        println("副构造函数2被调用")
    }
}

1.3 继承的格式

格式:

class 新的类名(无/参数) : 被继承类(无/参数){}

重写类的继承方法需要关键字override,并且被重写的方法需要有open关键字修饰。

kotlin 复制代码
open class Person(val name:String,val age:Int) {

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

}

class Student(val sno:String,val age1:Int) : Person(sno,age1){
    override fun eat(){
        println("$name Dont $age")
    }
}

fun main(){
    var i = Student("JAck",19)
    i.eat()
}

kotlin规定,当一个类既有主构造函数又有次构造函数,所有的次构造函数都必须调用主构造函数。

kotlin 复制代码
open class Person(val name:String,val age:Int) {

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

}

class Student(val sno:String,val grade:Int,name: String,age: Int) : Person(name,age){
    //通过次构造函数调用主构造函数
    constructor(name: String,age: Int) : this("",0,name,age){

    }
    constructor() : this("",0)
}

fun main(){
    var i = Student("JAck",19)
    var p = Student("abs",1,"jaks",1)
    i.eat()
}

在这里this的用法是什么??

在这个类中,this关键字的使用是为了调用不同构造函数的目的。

具体来说,这个Student类具有以下构造函数:

  1. 主构造函数:Student类的主构造函数接受四个参数,分别是snogradenameage。这些参数用于初始化类的属性,其中snograde是继承自Person类的属性,而nameage是本类的属性。
  2. 第一个副构造函数:这个副构造函数接受两个参数,即nameage,它通过使用this关键字来**调用了主构造函数,**传递了空字符串 ""0 作为参数,这意味着它将调用主构造函数以初始化snograde属性,并且将传递的nameage参数用于初始化本类的属性。
  3. 第二个副构造函数:这个副构造函数不接受任何参数,它同样通过使用this关键字来**调用了第一个副构造函数,**传递了空字符串 "" 和零 0 作为参数,从而也会触发调用主构造函数,并且初始化本类的属性。

2、接口

接口的基本用法和java一致,但是在kotlin中允许代码对接口中的方法进行默认实现。

kotlin 复制代码
interface Study{
    fun readBooks()
    fun doHomework(){
        println("ANT")
    }
}

3、数据类------Java中toString、equals、hashCode的省略

在kotlin中我们不必写这些繁琐的方法,直接使用关键字data即可,代码如下:

kotlin 复制代码
//使用data修饰
data class Cellphone(val brand: String,val price: Double) {
}

fun main(args: Array<String>) {

    val cellphone1 = Cellphone("Samsung",1299.99)
    val cellphone2 = Cellphone("Samsung",1299.99)
    println(cellphone1)
    println( "cellphone1 == cellphone2 ??   "+(cellphone1 == cellphone2))
}

Kotlin会根据主构造函数中的参数帮你将equals(),hash Code(),toString()等固定且无实际逻辑意义的方法自动生成。

4、单例类------object关键字

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

kotlin 复制代码
object Singleton {
    fun singletonTest(){
        println("This is a OneInstance")
    }
}

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

可以看出在Kotlin中我们不需要私有化构造函数。

5、Lambda编程

5.1 集合的创建和遍历

5.11 List集合

5.111、listOf

一般情况下我们需要使用这种方式初始化:

kotlin 复制代码
    val list = ArrayList<String>()
    list.add("Apple")
    list.add("Banana")
    list.add("Orange")

在kotlin中提供了一个内置的listOf函数来简化初始化集合的写法。

kotlin 复制代码
val list = listOf<String>("Apple","Banana","Orange")

当然我们也可以使用for-in遍历这个集合,并且Kotlin会自动补全fruit的类型。

kotlin 复制代码
    for(fruit in list){
        println(fruit)
    }

不过需要注意的是使用listOf创建的集合是一个不可变集合,就是该集合只能用于读取操作。

为什么会不可修改呢?

通过源码我们发现底层是数组的存储方式这样就能解释listOf`创建的集合只能用于读写操作了,同样他的查询效率也是比较高的。

5.112、mutableListOf

使用mutableListOf创建的集合是一个可变集合,可以对集合进行增删改查的操作。

查看源码可以发现创建的是一个Arraylist类型的集合。使用链表的形式存储。

5.12 Set类型

Set集合的用法基本上和List集合的一样,只不过创建集合的方式变成了setOf()mutableSetof()

5.13 Map类型

Map类的的基本创建方法和Java中是一致的。

kotlin 复制代码
    val map = HashMap<String, Int>()
    map.put("Apple",1)
    map.put("Banana",2)
    map.put("Orange",3)

当然kotlint在Map中同样提供了mapOfmutableMapOf方法用于集合的创建。Kotlin 的 mapOfmutableMapOf 在底层都使用哈希表(Hash Table)作为存储结构,mapOf是数组存储,mutableMapOfLinkedHash

kotlin 复制代码
val map = mapOf("Apple" to 1,"Banana" to 2,"Orange" to 3)

for((fruit,number) in map){
    println(fruit + number)
}

需要注意的时这里的to不是关键字而是一个infix函数。

Kotlin 中,infix函数允许你以更自然的方式使用中缀表示法调用函数。

5.2 集合函数式API

5.21 Lambda表达式的优化过程

{参数名:参数类型 -> 函数体}

kotlin 复制代码
val maxLengthFruit = list.maxBy({fruit:String -> fruit.length})
  • Kotlin规定当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到括号外面
kotlin 复制代码
val maxLengthFruit = list.maxBy(){fruit:String -> fruit.length}
  • 如果Lambda表达式是函数的唯一个参数,还可以将括号省略。而且kotlin具有出色的推到机制,我们还可以不声明参数类型。
kotlin 复制代码
val maxLengthFruit = list.maxBy{fruit -> fruit.length}
  • 并且当Lambda表达式参数列表只有一个参数时,也不必要声明参数名。可以使用it代替
kotlin 复制代码
val maxLengthFruit = list.maxBy{it.length}

5.22 常用API

  1. 计算最大长度:maxBy{it.length}

  2. 小写转大写:map { it.uppercase() }

  3. 过滤集合中的数据:filter函数

    kotlin 复制代码
    val newList = list.filter { it.length > 5 }.map { it.uppercase() }
  4. 判断集合是否存在至少一个元素满足要求:any.{}

  5. 判断集合是否所有元素都满足要求:all.{}

    kotlin 复制代码
    val anyResult = list.any{it.length >= 5}
    val allResult = list.all { it.length >= 10 }

6、空指针检查机制

Kotlin将空指针检查机制提到了编译时期,它默认所有的参数和变量都是非空的。

那么我们应该如何表示一个可空的类型呢?

6.1 ?

很简单在类名后加一个?代表可以是非空整形,例如:
I n t ? Int? Int?

但是此时就可能出现潜在的空指针异常风险,那么我们应该如何解决呢?Kotlin提供了一个辅助判空工具------?.。

6.2 辅助判空工具

6.2.1 ?.

例如这段代码:

kotlin 复制代码
if(A != null){
	A.do()
}

可以简化为这样的形式:

kotlin 复制代码
A?.do()

6.2.2 ?:

例如这段代码:

kotlin 复制代码
if(A != null){
	A
}else{
	B
}

可以简化为以下形式:

kotlin 复制代码
Val c = A ?: C

左边表达式不为空就返回左边的值,否则返回右边的值。

6.2.3 非空断言工具------!!

Kotlin的这种检查机制在某些情况下也会出现问题,例如:

此时是无法通过编译的,因为uppercase无法知道content不是空值。

此时就需要一种断言工具------!!

kotlin 复制代码
val upString = content!!.uppercase()

这是一种有风险的写法,意在告诉Kotlin,这里对象肯定不是空你不需要帮我检查空指针。

6.2.4 let辅助工具

这个函数提供了函数式API的编程接口,并将原始对象作为参数传递到Lambda表达式中。

kotlin 复制代码
obj.let{ obj1 ->
        //业务逻辑
       }

在这里,obj和obj1实际上是同一个对象。

如使用let修改这一段代码:

kotlin 复制代码
fun doStuday(study: Study?) {
    study?.doHomework()
    study?.readBooks()
}

修改结果如下:

kotlin 复制代码
fun doStuday(study: Study?) {
    study?.let {
        it.readBooks()
        it.doHomework()
    }
}

let是可以处理全局变量的判空问题但是if不行,因为if可以导致别的线程修改了这个全局变量。

相关推荐
Swift社区3 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht3 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht3 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe6 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin6 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python