此篇仅用来记录参考学习,如有错误还请指正
更新日期:2023/8/11
var
:定义变量,默认是private的val
:定义常量,默认是private的,可见性为private final static,并且会生成get(),set()方法,调用时通过方法访问。lateinit var
:延迟初始化变量,默认不允许为空,延迟但必须要初始化的变量const val
: 定义常量,相当于Java public final static 只能用在顶级属性,以及object对象的属性中,可直接访问,在定义常量时相较于val 减少了函数调用。fun
:定义方法,默认是public finalUnit
:默认方法返回值,类似于Java中的void,可以理解成返回一个没有意义的值vararg
:可变参数$
:字符串模板(取值),用于替代Java 字符串拼接操作in
:在某个范围中如
for (s in mTitles) {
//遍历mTitles
}
downTo
:递减,循环时可用,每次减1step
:步长,循环时可用,设置每次循环的增加或减少的量when
:Kotlin中增强版的switch,可以匹配值,范围,类型与参数is
:判断类型用,类似于Java中的instanceof()as
:可以用于对象的类型转换,取代Java中的强转open
:在Kotlin开发中类和方法默认不允许被继承和重写,等同于Java中用 final 修饰类和方法。如果在Kotlin 中类和方法想被继承和重写,需添加open 关键字修饰。!!
: 加在变量后如果对象为null,一定会报异常,与 ?相反,相当于直接用Java 的方式?
: :空合并操作符,相当于你的备用选项,如val len= value?.length ?: 0 ,如果value为null,则len等于你的默认值 0::
:表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法==
:用于比较两个变量值的大小,同Java 中equals()===
: 用于比较对象的地址是否相同"""
: 所见即所得,字符串在你编辑器中什么效果,显示出来就是什么效果inline
:通过内联化 lambda 表达式可以消除压栈出栈的开销noinline
:只想被(作为参数)传给一个内联函数的 lamda 表达式中只有一些被内联,你可以用 noinline 修饰符标记某些lambda表达式禁止内联,定义了noinline 的lambda表达式将不能直接调用 return ,否则会报错crossinline
:也可以限制lambda表达式将不能直接调用 return ,它与 noinline 的区别在于使用crossinline的lambda仍然是inline的*
:在 Kotlin 中,* 号代表展开操作符(Spread Operator),它可以用于将数组的元素作为多个参数传递给函数或构造函数。例如:
val array = arrayOfNulls<String>(2)
array[0] = "Hello"
array[1] = "World"
someFunction(*array)
下面有详细介绍
Kotlin中数字相关的内置类型有: byte、short、Int、Long、Float、Double
需要注意的:
?
可空类型 例如 :Int?
Any
Any即相当于 Java 中的Object类
所有非空类型的基类 Any
any 的可空类型: any?
Unit
当函数没有返回值时,使用Unit进行修饰
当函数没有返回值试 通常无需显示声明Unit 而是忽略不写
Unit本质是一个全局单例object实例对象,所以它是一个Any类型数据,它的效果等同于 Java 语言的void关键字
Nothing
表示一个永远不存在的值,可认为是 空类型
当函数返回值类型声明为Nothing时,表示该函数永远不会正常终止,通常表现为函数运行时永远会抛出异常
Nothing的这个特性通常有两方面用途:
Nothing与Unit的区别在于:Unit是一个真正的实例对象,其类型为Any,只是它表示返回值内容为空,但我们仍然能通过val ret = funcReturnsUnit()来获取函数返回值,而Nothing表示函数抛出异常,没有返回值。
object
在类中:
用object 修饰的类为静态类,里面的方法和变量都为静态的。
实现接口:
在java中实现接口为new callback(){ } 方式
而在Kotlin中实现接口方式之一是 object : callback { }
companion object
companion object只能定义在class中,并且一个class中只能定一个一个companion object。companion object作用域为代码块,里面成员和方法可通过companion object直接调用(companion object会生成一个companion对象)
?:
空合并操作符 相当于 else 或 default
takeIf
someObject?.takeIf{ status }?.apply{ doThis() }
类似于比if多个判空,而且后面不加?的话apply里的代码不管status false or true 一定会执行!!!
apply函数
apply函数扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象.
常用方式,用于对象的 配置,使用this引用对象
主要的作用:是可以用来简化初始化对象的功能。
特别需要注意的是apply函数中表示对象本身使用的是this关键字而不是it。
webSettings?.apply {
setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。
builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
displayZoomControls = true; //隐藏原生的缩放控件
blockNetworkImage = false;//解决图片不显示
loadsImagesAutomatically = true; //支持自动加载图片
defaultTextEncodingName = "utf-8";//设置编码格式
}
inline
内联
当一个函数被内联 inline 标注后,在调用它的地方,会把这个函数方法体中的所以代码移动到调用的地方,而不是通过方法间压栈进栈的方式。
换句话说:在编译时期,把调用这个函数的地方用这个函数的方法体进行替换。
inline 能带来的性能提升,往往是在参数是 lambda 的函数上。
inline 不适合在无参数的函数中, 适合在包含 lambda 参数的函数上。
下面是一个使用 inline 关键字定义的简单函数示例:
inline fun measureTimeMillis(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
在上面的代码中,measureTimeMillis 函数接受一个 Lambda 表达式参数,并返回其执行所需的时间。使用 inline 关键字定义该函数,它会在函数调用处直接将 Lambda 表达式中的代码拷贝下来,从而避免了函数调用的开销。
使用内联函数还可以帮助减少代码重复,提高代码的可读性和可维护性。下面是一个使用内联函数优化代码复用的示例:
inline fun <reified T : Activity> Context.startActivity() {
val intent = Intent(this, T::class.java)
startActivity(intent)
}
在上面的代码中,该内联函数扩展了 Context 类,可以直接启动指定类型的 Activity。使用 inline 关键字定义该函数,可以直接在函数调用处拷贝函数体中的代码。由于使用了泛型和 reified 关键字,该函数可以根据传入的参数类型自动推导 Class 对象,避免了代码中重复声明和转换类型的情况,提高了代码的可读性和可维护性。
noinline
noinline 是用来修饰函数参数的一个关键字。当一个函数参数被 noinline 修饰时,其对应的 Lambda 表达式将不能够被内联执行。
当编写高阶函数时,我们通常会接受一个 Lambda 表达式作为参数。默认情况下,Kotlin 编译器会尝试将这些 Lambda 表达式内联到函数中,以提高代码的执行效率。但是,在一些特殊情况下,我们可能需要 Lambda 表达式的特殊行为,例如将其作为一个参数传递给另一个函数等。这时可以使用 noinline 关键字来禁止编译器内联 Lambda 表达式。
inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (t in this) {
if (predicate(t)) {
result.add(t)
}
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { number ->
number % 2 == 0
}
}
在上面的示例中,我们定义了一个 filter() 函数,它接受一个 Lambda 表达式作为参数,并返回一个过滤后的列表。这个函数使用了 inline 关键字来告诉编译器,该函数的实现应该被内联。但是,我们在 Lambda 表达式参数前加上了 noinline 关键字,表示该 Lambda 表达式不应该被内联。
如果我们省略了 noinline 关键字,则编译器将会尝试内联 predicate 参数,这将导致代码出错。
总之,noinline 关键字用于指示编译器不要把一个 Lambada 表达式参数内联到高阶函数中去。这种情况通常在需要将 Lambda 表达式作为参数传递给另一个函数时出现。
crossinline
crossinline 是用来修饰函数参数的一个关键字。当一个函数参数被 crossinline 修饰时,其对应的 Lambda 表达式将不能够使用 return 关键字进行非局部返回。
当编写高阶函数时,我们通常会接受一个 Lambda 表达式作为参数。在 Lambda 中使用 return 关键字时,它通常会直接返回封闭函数,导致封闭函数中的代码无法得到执行。
fun doSomething(callback: () -> Unit) {
Thread {
callback()
}.start()
}
fun main() {
doSomething {
if (somethingBadHappened) {
return // 编译错误:return 不允许在这个上下文中使用
}
// 正常情况的处理逻辑
}
}
在上面的代码示例中,doSomething 函数接受一个 Lambda 表达式参数,并在新线程中执行它。当 Lambda 中发生了一些错误时,我们希望它能够非局部返回,以提前结束整个线程。但是,由于默认情况下 Lambda 是允许使用 return 关键字进行非局部返回的,这将导致编译错误。
这时可以使用 crossinline 关键字来解决这个问题,它告诉编译器对于在 Lambda 中使用 return 关键字时要有不同的处理。使用 crossinline 关键字修饰之后,虽然可以在 Lambda 中使用 return 关键字,但是它只能在 Lambda 函数自身中进行局部返回操作,而不能对其它外围函数进行非本地返回操作。
fun doSomething(crossinline callback: () -> Unit) {
Thread {
callback()
}.start()
}
fun main() {
doSomething {
if (somethingBadHappened) {
return@doSomething // 在 Lambda 函数自身中进行局部返回
}
// 正常情况的处理逻辑
}
}
在上面修改后的示例中,使用 crossinline 关键字修饰了 callback 参数,Lambda 中可以使用 return@doSomething 来在 Lambda 函数自身中进行局部返回,而不能直接返回封闭函数。
总之,crossinline 关键字用于修饰函数参数,可以在 Lambda 表达式中使用 return 关键字,但是只能在 Lambda 函数自身中进行局部返回操作,不能对其它外围函数进行非本地返回操作,以避免影响封闭函数中的代码执行。
run
函数
返回函数值是函数最后的一行,或者指定return表达式。
let
函数
调用某个对象的let函数,则将该对象作为函数的参数。在函数块中可以通过it指代对象。返回值是函数最后的一行,或者指定return表达
also
函数
调用某对象的also函数,在函数块内可以通过it指代该对象。返回值是对象的本身
with
函数
with函数和前面的几个函数的使用方式不同,它不再是以扩展的形式存在的,它是将对象作为函数的参数,在函数块内可以通过this指定该对象。返回值是函数最后的一行,或者指定return表达
as
如:
val any: Any = "abc"
any as String
print(any.length.toString())
by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED)
委托,懒加载
如:
//只有使用A时 {}里的代码才会被加载,并且下面代码是双重校验锁实现的单例
A by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { B() }
LazyThreadSafetyMode有三种:
密封类(Sealed class)
密封类(Sealed class)是一种特殊的类,用于表示受限的类继承结构。它允许定义一个类的层次结构,并限制该层次结构的子类集合。密封类的主要特点是其子类必须位于密封类的同一文件内,这使得密封类能够对可能的子类进行严格控制。
sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
fun handleResult(result: Result) {
when (result) {
is Success -> println(result.data)
is Error -> println(result.message)
}
}
sealed class Result {
class Success(val data: String) : Result()
class Error(val message: String) : Result()
}
Kotlin 可以对一个类的属性和方法进行扩展。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
如:
fun receiverType.functionName(params){
body
}
receiverType:表示函数的接收者,也就是函数扩展的对象
functionName:扩展函数的名称
params:扩展函数的参数,可以为NULL
数组(Array):用于存储一组相同类型、固定长度的数据。
列表(List):用于存储一组有序的数据,支持重复元素。Kotlin 中的列表可以使用可变类型(MutableList)和不可变类型(List)来表示。
集合(Set):用于存储一组无序、不重复的数据。Kotlin 中的集合可以使用可变类型(MutableSet)和不可变类型(Set)来表示。
映射(Map):用于存储一组键值对数据,其中每个键都是唯一的。Kotlin 中的映射可以使用可变类型(MutableMap)和不可变类型(Map)来表示。
字符串(String):用于存储一组字符序列。
区间(Range):用于表示一段连续的数值区间,可以使用整型、字符型等类型来表示。Kotlin 中的区间可以使用闭区间(a…b)和半开区间(a until b)来表示。
字符串模板(String Template):用于将字符串中的某些部分动态地替换为变量或表达式的值。
元组(Tuple):用于将多个值组合在一起,形成一个不同类型、不同含义的元素集合。
Mutable和非Mutable的区别,以MutableList为例:
可变性:MutableList 是可变类型的列表,支持添加、删除、修改元素等操作;而 List 是不可变类型的列表,不能添加、删除、修改元素。
接口: MutableList 实现了 List 接口,因此可以使用 List 接口中的方法和属性,如 get(),size(),contains() 等,同时还额外提供了可以修改列表元素的方法,包括 add(),remove(),set() 等。
效率:MutableList 操作效率较高,因为它是基于可修改的指针来实现的,而 List 是不可修改的,因此需要在每次操作时都创建一个新的不可变列表对象,效率较低。
在实际开发中,如果需要频繁修改列表元素,建议使用 MutableList,因为它提供了更好的性能和更灵活的数据操作;如果只需要访问列表元素而不需要修改,可以考虑使用 List,因为它更安全、更稳定、更可靠。
object和companion object 区别
companion object 的定义完全属于类的本身,所以 companion object 肯定是不能脱离类而定义在全局之中。它就像 Java 里的 static 变量,所以我们定义 companion object 变量的时候也一般会使用大写的命名方式。
同时,和 object 类似,可以给 companion object 命名,也可以不给名字,这个时候它会有个默认的名字: Companion ,而且,它只在类里面能定义一次。
未完待续…