一篇文章带你学会Kotlin
都2023年了,新建的项目还是Java
项目,或者你还在写Java
样式的Kotlin
项目,仔细看完这篇文章,带你从Java
转到Kotlin
,带你学会Koltin
,从入坑到脱坑
为什么要学习Kotlin
Kotlin
是Andorid
官方推荐语言- 最年来
Google
发布很多新玩意,都是Kotlin
写的,对Kotlin
支持比较友好 Compose
你不会Kotlin
怎么学习- 一些大型开源项目,比如
Okhttp
,Retrofit
,Glide
都改成了Kotlin版本 - 使用协程,让你切换线程更加方便,摆脱回调地狱
- 让你的代码更简洁
综上所示,笔者认为,Kotlin
如今是一名Android开发工程师所必须要掌握的技能,但是偏偏还是有很多人不用,不学,所以就写下来这篇文章,带你快速入门Kotlin
,也算是对自己知识的一遍巩固
基础
何为Kotlin
,笔者认为是如何快速定义变量,常量,new
一个对象,调用一个方法,来看一下Java
是怎么做的
int a = 10;
a = 11;
TextView textView = new TextView(context);
textView.setText(a);
嗯,还是比较简洁的,但是还可以更简洁,看一下相同的代码使用Kotlin
如何完成
fun Test(context: Context?) {
var a = 10
a = 11
val textView = TextView(context)
textView.text = a.toString()
}
解释一下,Kotlin
定义常量是val
,变量为,var
,什么类型,根本不需要,它会通过后面得内容自动推导出来是什么类型的,但是从本质来说,Kotlin
是还是强类型语言,只不过编译器会自动推导出来他真的类型而已,然后是不用写new
关键字了,不用写;结尾了,get
、set
方法也不用写,直接等,实际上还是调用了真的get
和set
,原因是通过了属性访问器(accessor)的语法糖形式直接使用等号进行赋值和获取
类
接下来看一下类,点即创建一个Kotlin类型得File,会出来如下弹框
Class
和Java
的Class
没什么两样,一般就是一个类File
如果当一个Class
中有两个同级别的类,这个时候就会变为File
,这个一般在写扩展函数的时候使用,扩展函数后面会讲到Interface
和Java
的Interface
一样,一个接口Sealed interface
封闭接口,防止不同model之间的互相调用,比如你再B Model
中定义B Sealed interface
,那么你只能在B Model
中使用这个接口,除此之外,还是使用此接口完成多继承的操作Data class
实体类,和Java
实体类有什么不同呢,不用写get
,set
方法和普通Kotlin Class
有什么不同呢,首先是必须要有有参构造方法,然后重写了hashCode
和equals
方法Enum class
枚举类,和Java
一样Sealed class
和Sealed interface
差不多,都是限制只能在一个Model
中使用Annotation
定义一个注解Object
这个笔者最喜欢,常用来定义一个单例类,相当于Java的饿汉式 其中比较常用的有Class
,Data class
,Object
总的来说还是比Java的类型多一点
返回值
为什么要单写一个返回值,因为Kotlin
的所有方法都有返回值,常规的就不说,看下面代码
val a: Unit = printA()
private fun printA() {
print("A")
}
这里面定义了函数,没有定义任何返回值,但是其实的类型是Unit
,我的理解是Unit
就代表它是一个函数类型和String
一样都是Koltin
类型中的一种,明白了这点就可以理解接下来的操作
val a = if ( x > y) x else y
相当于Java
的三元换算符,如果x
大于y
就等于x
,否则就等于y
,对了Kotlin
是没有三元换算符这个概念的,如果要达到相同效果只有使用if...else...
,同理when
也同样适用于这种操作
还是一种建立在编译器类型推导的基础上一种写法
private fun getA() = {
val a = 0
a
}
这个函数的意义就是返回了一个数字a
,为什么没有return
它却拥有返回值返回值,请注意看这个=
号,编译器给他推导出来了,并且通过lamaba
返回值 还有一个非常特殊的返回值 Nothing 什么意思呢 不是null 就是什么也没有 具体可以看一下这篇文章
NULL安全处理
Kotlin是一个null安全的语言下面详细看一下
//定义一个变量
private var a: String? = null
String后面有个?代表,这个变量是可能为null的那么就需要在使用的时候处理它,不然会报错
if (a != null){
//不为null
}else{
//为null
}
//或者加上!!,代表这个时候肯定不为null,但是一般不建议这样写,玩出现null,就会空指针异常
a!!
当然如果确定使用的时候肯定部位null也可以这样写
private lateinit var a: String
代表定义了一个变量,我不管之前对它做了什么操作,反正我使用的时候,它一定是不为null的,当然与之的对应的还有by lazy
延迟初始化,当第一次使用的时候才会初始化,比如这样,当我第一次调用a
的时候,by lazy
的括号内容即委托就会执行给a
赋值,值得注意的是by lazy
不是绑定在一起的 也可以只使用by
意思也是委托,不过要单独写一个委托的实现
private val a: String by lazy { "123" }
扩展函数
扩展函数可以说是Kotlin比较爽的部分,他的本质其实是一个Kotlin的静态方法然后返回了它原本得了类型,比如这段代码
fun String?.addPlus(number: Int): String? {
if (this == null){
return null
}
return this.toInt().plus(number).toString()
}
扩展了String的方法,在String的基础上添加了一个addPlus
方法,然后接受一个number
参数,然后强转为int
类型之后加上number
并返回,这样扩展不用继承也可以在一些类中添加方法,减少了出BUG
的可能性
val str = "1"
val str2 = str.addPlus(2)
看一这段代码的本质,可以知道Koltin和Java是可以互相转换的
public static final String addPlus(@Nullable String $this$addPlus, int number) {
return $this$addPlus == null ? null : String.valueOf(Integer.parseInt($this$addPlus) + number);
}
可以看到,扩展函数的本质是一个静态方法,并且多了一个String类型的参数,这个参数其实就是扩展函数类的实体,利用扩展函数可以实现简化很多操作,比如金额计算就可以扩展String,然后接收一个String,又或者是给textView添加一个功能,同样可以使用,我认为它差不多就是简化了静态工具类的使用,让其更方便,更快捷
扩展函数二 let also run apply
还记得上面提到的Kotlin对于null的判断吗,其实拥有更快快捷的方法就是使用扩展函数看这一段代码
fun stringToInt(str: String?): Int {
return str?.let {
it.toIntOrNull() ?: -1
} ?: run {
-1
}
}
一段链式调用,这也是Kotlin的特性之一,后续有时间会讲,链式调用有好处也有坏处,好处是可以更好的代码,坏处是一旦使用了过长的链式调用,后期代码维护就会很麻烦,为什么我会知道,因为我就写过,后期维护的时候痛不欲生,开发一时爽维护两行泪,但是适量使用这种,会让你的代码变得更简洁,维护更方便,主要还是看工程师的把握度。
好了具体看一下代码做了什么工作,首先定义了一个stringToInt
的函数,然后接受了一个String
参数,这个参数可以为null
,然后判断是否等于null
,等于null
返回-1,不能转化为int
返回-1,可以正常转化返回int
值 Ok 先解释一下如何利用扩展函数做null
判断 ?.let
代表如果str
这个值不为null
,那么就执行it.toIntOrNull() ?: -1
,否则这里用 ?:
来处理即为null 就走run 然后返回-1
- let 提供了一个以默认值it的空间,然后返回值的是当前执行控件的最后一行代码
- also 提供了一个以默认值it的空间,然后返回值是它本身
- run 提供了一个this的命名空间,然后返回最后一行代码
- apply 提供了一个this的命名空间,然后返回它本身 这里贴出Kotlin的实现,源码其实很简单
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
代码其实很简单,有兴趣可以自己随便玩玩,这里就以apply
为例分析一下,首先返回一个泛型T
,这个T
就是调用者本身,然后接口了一个black
的函数,这个函数就是实际在代码中apply
提供的空间,然后执行完成后,然后调用者本身,这样就和上文对应上了,apply 提供了一个this的命名空间,然后返回它本身,也可以仿照实现一个自己myApply
哈哈哈,
结语
这篇文章其实原本想写的非常多,还有很多关键字没有介绍,比如by,inline
,受限于篇幅问题,暂时把最基础的写了一写,以后会逐步完善这个系列,在写的过程,也有一些是笔者使用起来相对来说比较少的,就比如Sealed interface
这个接口,之前就完全不理解,在写的时候特意去查询了一下资料,然后自己测试了一番,才理解,也算是促进了自己学习,希望可以共同进步
链接:https://juejin.cn/post/7244095157673394232
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。