前言
kotlin学习第五篇文章!
历史文章
[AS3.6.1]Kotlin学习笔记1(基本声明,函数,条件)
[AS3.6.1]Kotlin学习笔记2(常量,数组,修饰符)
[AS3.6.1]Kotlin学习笔记3(简化操作,泛型)
[AS3.6.1]Kotlin学习笔记4(接口,Lambda,协程)
类
前面我们讲过一些类的内容,比如Any
就是Object
,使用:
替代java中的继承extends
和接口implements
,多个之间使用,
隔开等。现在我们来具体了解下类,包括一些说明和object
、data
等。
类的说明
- kotlin的
Any
就是java的Object
- kotlin中类和函数一样默认都是用
public final
修饰的,需要继承的时候要加上open
- java中的继承和接口在kotlin中均用
:
替代,多个用,
隔开 - 获得类class,kotlin类为
KClass::class
java类为JClass::class.java
我们在简化中说过 以下简化
class KClass {
var name: String
constructor(name: String){
this.name = name
}
}
//可以简化成
class KClass constructor(var name: String){
}
class KClass(var name: String){
}
如果我需要设置一个私有的构造函数类,写法如下
class KClass private constructor(var name: String){
}
嵌套类
java中我们也经常会写到,写法和java差不多
class KClass{
...
class KClass2{
...
}
}
//调用
val kClass2 = KClass.KClass2()
内部类
因为我们上面说过类都是默认使用public final
修饰的,所以想要一个内部类就需要使用inner
修饰
class KClass{
...
inner class KClass3{
...
}
}
//直接调用
//报错提示 Constructor of inner class KClass3 can be called only with receiver of containing class
val kClass3 = KClass.KClass3()
对象类 object
这个我们之前也提过,object类就是对象类直接生成一个饿汉式的单例类。这边再次说下使用情况
那在实际使用中,在
object
、companion object
和top-level
中该选择哪一个呢?
简单来说按照下面这两个原则判断:
如果想写工具类的功能,直接创建文件,写 top-level「顶层」函数。
如果需要继承别的类或者实现接口,就用 object 或 companion object。
数据类 data
这个我们在协程的时候使用了下,也没说明。这边就开始讲下数据类。
data
类似于lombok的@Data
注解,可以自动生成toString()
,equals()
,hashcode()
,copy()
等方法。
data class KData(
val str: String,
val int: Int,
val `data`: Any
)
//使用
val kData1 = KData("data1", 5, false)
val kData2 = kData1.copy(str = "data2", data = "Any2")
val (str, int, data) = kData1
println(kData1) //KData(str=data1, int=5, data=false)
println(kData2) //KData(str=data2, int=5, data=Any2)
println("str = $str, int = $int, data = $data") //str = data1, int = 5, data = false
我们可以看到,data
生成的数据类非常的好用,可以直接copy数据,并且还支持解构。
密封类 sealed
先来看下说明
密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。
声明一个密封类,使用sealed
修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。
sealed
不能修饰interface
,abstract class
(会报 warning,但是不会出现编译错误)
sealed class House
class Apartment : House()
class Hotel : House()
//检查方法
private fun check(house: House) = when(house){
is Apartment -> println("是 公寓")
is Hotel -> println("是 酒店")
}
//使用
check(Apartment()) //是 公寓
check(Hotel()) //是 酒店
我们发现这就是高级的枚举使用,还是可以设置属性的枚举。并且我们可以看到使用when
的时候不在需要设置else
了因为密封类已经帮我们判断了所有的情况。
函数
我们前面说过函数相关的内容,kotlin的函数有嵌套函数
、扩展函数
、高阶函数
、内联函数
。
和类一样,kotlin的函数默认都是public final
修饰,想要能够被重构需要加入open
,如果是继承父类的方法还要加上overrider
。
我们还知道kotlin的函数是可以设置默认值的
fun info(id: Int, name: String = "张三", sex: String = "男"){
println("id = $id, name = $name, sex = $sex")
}
//使用
info(1) //id = 1, name = 张三, sex = 男
info(2, "李四") //id = 2, name = 李四, sex = 男
info(3, "王五", "女") //id = 3, name = 王五, sex = 女
那么如果混合开发中我们需要让java也可以实现,只需要加入注释@JvmOverloads
@JvmOverloads
fun info(id: Int, name: String = "张三", sex: String = "男"){
println("id = $id, name = $name, sex = $sex")
}
这样我们就不需要为了让java可以使用写多个重载函数了。
嵌套函数(本地函数)
之前提过,这边就写下例子
fun aFun(){
...
fun bFun(){
...
}
bFun()
}
扩展函数
扩展函数算是kotlin中很好用的特性,可以在不修改原来类,函数的情况直接加入新的函数或者重写函数。
比如我们为CharSequence
加入一个toList
函数
fun CharSequence.toList(): List<Char>{
val list = ArrayList<Char>()
for (c in this) {
list.add(c)
}
return list
}
//使用
val chars = "abcdefg".toList()
for (char in chars) {
print("$char ") //结果 a b c d e f g
}
高阶函数
在[AS3.6.1]Kotlin学习笔记4(接口,Lambda,协程)接口中我们已经对高阶函数有了一定的理解了。这边复述一下就是可以让函数传递的变量也是函数。简单示例如下
fun aFun(block: () -> Unit){
block()
}
fun bFun(){
println("bFun")
}
//使用
aFun {
println("aFun") //aFun
}
aFun {
bFun() //bFun
}
内联函数
通过 inline
修饰的函数为内联函数
看下面的例子
fun cFun(){
var x = 1
dFun()
println("cFun $x")
}
inline fun dFun(){
var y = 2
var z = 3
println("dFun $y $z")
}
调用cFun如果没有加inline
是会增加内存和性能的开销,为什么呢?因为加入inline
后实际真实的cFun代码是如下
fun cFun(){
var x = 1
var y = 2
var z = 3
println("dFun $y $z")
println("cFun $x")
}
加入内联之后,编译后kotlin会自动将内联的代码直接写在cFun中,并不会有两个方法。
而且实际内联函数一般都用于lambda中,但注意的是,要内联的函数带的lambda不宜过大,否则会造成生产class文件过大,所以当内联函数太大的时候可以使用noinline
取消lambda的内联。
总结
关于类和函数的补充!