注册

一篇文章带你学会Kotlin

都2023年了,新建的项目还是Java项目,或者你还在写Java样式的Kotlin项目,仔细看完这篇文章,带你从Java转到Kotlin,带你学会Koltin,从入坑到脱坑

为什么要学习Kotlin

  1. KotlinAndorid官方推荐语言
  2. 最年来Google发布很多新玩意,都是Kotlin写的,对Kotlin支持比较友好
  3. Compose你不会Kotlin怎么学习
  4. 一些大型开源项目,比如OkhttpRetrofitGlide都改成了Kotlin版本
  5. 使用协程,让你切换线程更加方便,摆脱回调地狱
  6. 让你的代码更简洁

综上所示,笔者认为,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关键字了,不用写;结尾了,getset方法也不用写,直接等,实际上还是调用了真的getset,原因是通过了属性访问器(accessor)的语法糖形式直接使用等号进行赋值和获取

接下来看一下类,点即创建一个Kotlin类型得File,会出来如下弹框

image.png

  • Class 和JavaClass没什么两样,一般就是一个类
  • File 如果当一个Class中有两个同级别的类,这个时候就会变为File,这个一般在写扩展函数的时候使用,扩展函数后面会讲到
  • Interface 和JavaInterface一样,一个接口
  • Sealed interface 封闭接口,防止不同model之间的互相调用,比如你再B Model中定义 B Sealed interface,那么你只能在B Model中使用这个接口,除此之外,还是使用此接口完成多继承的操作
  • Data class 实体类,和Java实体类有什么不同呢,不用写getset方法和普通Kotlin Class有什么不同呢,首先是必须要有有参构造方法,然后重写了hashCodeequals方法
  • Enum class 枚举类,和Java一样
  • Sealed class 和Sealed interface差不多,都是限制只能在一个Model中使用
  • Annotation 定义一个注解
  • Object 这个笔者最喜欢,常用来定义一个单例类,相当于Java的饿汉式 其中比较常用的有ClassData 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这个接口,之前就完全不理解,在写的时候特意去查询了一下资料,然后自己测试了一番,才理解,也算是促进了自己学习,希望可以共同进步


作者:zhaojf
链接:https://juejin.cn/post/7244095157673394232
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册