Kotlin入门

本文介绍了Kotlin与Java的差异,包括访问权限、构造函数、枚举等方面的对比。重点讲解了Kotlin的语法糖,如空安全检查、默认参数、函数赋值、类扩展方法等,并详细阐述了let、with、run、apply、also五种扩展函数的使用场景和功能。还简要提及了Kotlin中的协程概念及其启动方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

与Java的差异

常见差异

比较内容KotlinJava
this对象this@MainActivityMainActivity.this
MainActivity::class.javaMainActivity.class
继承class MainActivity : AppCompatActivity()
在 Kotlin 中被继承类必须被open关键字修饰(默认是final的)
public class MainActivity extends AppCompatActivity
变量不可变变量 val intent = Intent()
可变变量 var intent = Intent()
Intent intent = new Intent();
静态常量const val TEXT = “text”static final String TEXT = “text”;
方法无返回值 fun doAction() {}
有返回值 fun getNumber() : Int { return 5}
或者 fun getNumber() = 5
无返回值 void doAction() {}
有返回值 int getNumber() {return 5;}
类型判断“text” is String“text” instanceof String
字符串占位val number = 100
val text = “数量 = ${number}”
int number = 100;
String text = String.format(“数量 = %d”, number);
容器val array = arrayOf(1,2,3)
val list = listOf(1,2,3)
val map = mapOf(Pair(1, “one”), Pair(2, “two”), Pair(3, “three”))
for循环val array = arrayOf(1,2,3)
for (i in array.indices) { pringln(i) }

访问权限

Kotlin作用域Java作用域
public与java相同public所修饰的类、变量、方法,在内外包均具有访问权限
internal在Kotlin有一个模块的概念,定义为internal的对象,同一模块是可以相互访问的protected这种权限是为继承而设计的,protected所修饰的成员,对所有子类是可访问的,但只对同包的类是可访问的,对外包的非子类是不可以访问
protected与java相同default只对同包的类具有访问的权限,外包的所有类都不能访问
private与java相同private私有的权限,只对本类的方法可以使用

Kotlin语法

判断器

var count = 1

when (count) {
    0 -> {
        println(count)
    }
    in 1..2 -> {
        println(count)
    }
    else -> {
        println(count)
    }
}

构造函数

class MyView : View {

    constructor(context : Context) : this(context, null) {
    }

    constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0) {
    }

    constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr) {
    }
}

枚举

enum class Sex (var isMan: Boolean) {
    MAN(true), WOMAN(false)
}

匿名内部类

object:Callback {

    override fun onSuccess() {
    }

    override fun onFail() {
    }
}

内部类

inner class InnerClass{
}

内部类访问外部变量

var name = "外部变量"

inner class MyTask {

    var name = "内部变量"

    fun show() {
        println("我是内部变量:$name,我是外部变量${this@MainActivity.name})
    }
}

抽象类

abstract class BaseActivity : AppCompatActivity(), Runnable {
    abstract fun init()
}

静态变量和静态方法
在Kotlin中被称为伴生对象

companion object ToastUtils {
    var sToast : Toast? = null

    fun show() {
        sToast!!.show()
    }
}

可变长参数

fun add(vararg array: Int) : Int {
    var count = 0
    array.forEach {
        count += it
    }
    return count
}

构造代码块

class MainActivity : AppCompatActivity() {

    var number = 0

    init {
        number = 1
    }
}

静态代码块

class MainActivity : AppCompatActivity() {

    companion object {
        
        var number = 0
        
        init {
            number = 1
        }
    }
}

Kotlin语法糖

无需 findViewById

xml中
<TextView
    android:id="@+id/tv_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!" />
代码中
tvContent.text = "我不用findViewById"

Lambda表达式
和Java8中相同使用即可。

tv_content.setOnClickListener { v -> v.visibility = View.GONE }

如果Lambda表达式中只有一个变量,可以直接用it来代替他。

tv_content.setOnClickListener { it.visibility = View.GONE }

函数赋值变量
在 Kotlin 语法中函数是可以作为变量进行传递的。

var addResult = fun(number1 : Int, number2 : Int) = number1 + number2
println(addResult(1, 2))

空安全检查
Kotlin 对空对象进行了限定,必须在编译时处理对象是否为空的情况,不然会编译不通过。
当对象可为空时,必须先判断对象是否为空。

var text : String? = null
println(text?.length)
返回null

如果确定一个可能为空的对象不为空时,可以使用2个 ! 跳过判断。

var text : String? = "text"
pringln(text!!.length)

如果跳过判断,当对象为空时,还是会抛出NPE。

函数支持默认参数
在Java中,我们定义一个可能包含多个不同参数的方法时,我们需要进行多次重载定义。

public void toast(Context context) {
	toast(context, "默认toast");
}

public void toast(Context context, String text) {
    toast(context, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text, int duration) {
    Toast.makeText(context, text, duration).show();
}

在Kotlin中我们只需要给参数设定默认值,只需要定义一个方法即可。

fun toast(context : Context, text : String = "默认toast", time : Int = Toast.LENGTH_SHORT) {
    Toast.makeText(context, text, time).show()
}

当调用方法且参数缺省时,就会使用默认参数。

toast(this)
toast(this, "弹个吐司")
toast(this, "弹个吐司", Toast.LENGTH_LONG)

类扩展方法
可以在不用继承的情况下对扩展原有类的方法。

例如判断String是否为空串
fun String.isEmpty() = return TextUtils.isEmpty(this)
println("text".isEmpty)

扩展函数
扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数。

  1. let函数
            在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。
    最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。
普通写法
mVideoPlayer?.setVideoView(course_video_view)
mVideoPlayer?.setControllerView(course_video_controller_view)
mVideoPlayer?.setCurtainView(course_video_curtain_view)
let写法
mVideoPlayer?.let {
   it.setVideoView(course_video_view)
   it.setControllerView(course_video_controller_view)
   it.setCurtainView(course_video_curtain_view)
}
  1. with函数
    with函数是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。
定义一个类
class Person(var name : String, var age : Int)
普通写法
val person = Person("Jack", "30)
println("我叫${person.name},今年${person.age}岁")
with写法
with(Person("Jack", 30)){
    print("我叫${name},今年${age}岁")
}

        适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。

普通写法
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${item.name}"
    holder.ageView.text = "年龄:${item.age}"
}
with写法
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    val item = getItem(position)?: return
    with(item){
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}
  1. run函数
            实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式。
普通写法
val textView = TextView(context)
textView.text = "我是普通的"
textView.textSize = 18f
textView.setTextColor(Color.RED)
run写法
val textView = TextView(context)
textView.run {
    text = "我是run函数的"
    textSize = 18f
    setTextColor(Color.RED)
}
这里的textView的类型是Unit
  1. apply函数
            从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
apply写法
val textView = TextView(context).apply {
    text = "我是run函数的"
    textSize = 18f
    setTextColor(Color.RED)
}
这里的textView的类型是TextView

使用apply进行多层级判空

定义一个类
class People(var body: Body? = null, var mind: Mind? = null) {

    class Body(var trunk: Trunk? = null, var organ: Organ? = null, var bodyFluids: BodyFluids? = null) {
    
        class Trunk(var head: Head? = null, var hand: Hand? = null, var foot: Foot? = null) {
            inner class Head {}
            inner class Hand {}
            inner class Foot {}
        }

        class Organ {}
        
        class BodyFluids {}
    }

    class Mind {}
}
普通写法
val people = People()
if (people.body == null || people.mind == null) {
    return
}
if (people.body!!.trunk == null) {
    return
}
print("我是手对象${people.body!!.trunk!!.hand}")
apply写法
people.body.apply { }?.trunk.apply { }?.apply {
    print("我是手对象$hand")
}
  1. also函数
            also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身。
            适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用。
普通写法
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}
also写法
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

五种函数的使用场景
五种函数的使用场景
携程
子任务协作运行,优雅的处理异步问题解决方案。
协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。

引入携程包
kotlin {
    experimental {
        coroutines 'enable'
    }
}

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'
}

协程的三种启动方式

runBlocking:T     

launch:Job

async/await:Deferred
  1. runBlocking
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
runBlocking {
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000) // 因为 Activity 最长响应时间为 15 秒
    println("测试延迟结束")
}
println("测试结束")
17:02:08.686 System.out: 测试是否为主线程true
17:02:08.686 System.out: 测试开始
17:02:08.688 System.out: 测试是否为主线程true
17:02:08.688 System.out: 测试延迟开始
17:02:28.692 System.out: 测试延迟结束
17:02:28.693 System.out: 测试结束

runBlocking 运行在主线程,过程中 App 出现过无响应提示,由此可见 runBlocking 和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码。

  1. launch
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch {
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:19:17.190 System.out: 测试是否为主线程true
17:19:17.190 System.out: 测试开始
17:19:17.202 System.out: 测试结束
17:19:17.203 System.out: 测试是否为主线程false
17:19:17.203 System.out: 测试延迟开始
17:19:37.223 System.out: 测试延迟结束

这种效果类似 new Thread()。

  1. async
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
async {
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:29:00.694 System.out: 测试是否为主线程true
17:29:00.694 System.out: 测试开始
17:29:00.697 System.out: 测试结束
17:29:00.697 System.out: 测试是否为主线程false
17:29:00.697 System.out: 测试延迟开始
17:29:20.707 System.out: 测试延迟结束

执行结果和看上去和launch一样。

再执行以下这个代码
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
val async = async {
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
    return@async "666666"
}
println("测试结束")

runBlocking {
    println("测试返回值:" + async.await())
}
17:50:57.117 System.out: 测试是否为主线程true
17:50:57.117 System.out: 测试开始
17:50:57.120 System.out: 测试结束
17:50:57.120 System.out: 测试是否为主线程false
17:50:57.120 System.out: 测试延迟开始
17:51:17.131 System.out: 测试延迟结束
17:51:17.133 System.out: 测试返回值:666666

可以看到,最后的async是有返回值的,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值