[Android] Kotlin Basic Grammar Detailed (Quick Start)

foreword

In Google I/O 2017, Google announced Kotlin as the official Android development language. Kotlin is a statically typed programming language that runs on the Java virtual machine. Kotlin can be compiled into Java bytecode or JavaScript, and it can run perfectly in the Java environment.

The Kotlin language mainly has the following characteristics:

1. Simple and advanced syntax 2.
Null pointer safety
3. Fully compatible with Java : Kotlin and Java can be mixed for development
4. Functional programming : extended functions, support for Lambda expressions
5. Type inference : no need to specify the type
6. Powerful Coroutines : Handle threading issues
7. Delegate : Lazy loading, improve loading speed
8. Immutable data
9. No need to findViewById to get controls : Not recommended in some places

Kotlin can be said to be a collection of everyone's strengths, which greatly improves the development efficiency of Android native code, and is very popular.


1. Kotlin environment construction

When creating a project project, directly check the Include Kotlin support option and select the Kotlin language.
If it is an old Java project, Android Studio needs to install the Kotlin plug-in, and manually configure gradle at the same time, the configuration is as follows:

Add the following code under the gradle of the app module:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

Add the following code under the gradle of the project module:

ext.kotlin_version = '1.4.21'
dependencies {
    
    
	classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

The following is the specific introduction and use of the Kotlin language, and it is compared with the Java language for easy understanding.

Two, the object

How to write Java

MainActivity.this

Writing in Kotlin

this@MainActivity

Three, class

How to write Java

MainActivity.class

Writing in Kotlin

MainActivity::class.java

4. Inheritance

How to write Java

public class MainActivity extends AppCompatActivity {
    
    
    
}

Writing in Kotlin (inherited classes in Kotlin must be modified with the open keyword)

class MainActivity : AppCompatActivity() {
    
    
    
}

5. Variables

How to write Java

Intent intent = new Intent();

Writing in Kotlin

var intent = Intent()

6. Constants

How to write Java

final String text = "";

Writing in Kotlin

val text = ""

7. Static constants

How to write Java

public class MainActivity extends AppCompatActivity {
    
    

    static final String text = "";
}

Kotlin's writing method (note that static variables should be defined above the class)

const val text = ""

class MainActivity : AppCompatActivity() {
    
    

}

8. Definition method

How to write Java

public void test(String message) {
    
    

}

Kotlin's writing method (Unit has the same effect as void)

fun test(message : String) : Unit {
    
    

}
// 在 Kotlin 可以省略 Unit 这种返回值
fun test(message : String) {
    
    

}

Nine, overload method

How to write Java

public class MainActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
    }
}

Writing in Kotlin

class MainActivity : AppCompatActivity() {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
    }
}

10. Basic Data Types

How to write Java

int i = 1;
long l = 2;
boolean b = true;
float f = 0;
double d = 0;
char c = 'A';
String s = "text";

Writing in Kotlin

var i : Int = 1
var l : Long = 2
var b : Boolean = true
var f : Float = 0F
var d : Double = 0.0
var c : Char = 'A'
var s : String = "text"
// 更简洁点可以这样,自动推倒类型
var i = 1
var l = 2
var b = true
var f = 0F
var d = 0.0
var c = 'A'
var s = "text"

11. Comparison type

How to write Java

if ("" instanceof String) {
    
    

}

Writing in Kotlin

if ("" is String) {
    
    

}

12. Converter

How to write Java

int number = 100;
System.out.println(String.format("数量有%d", number));

Writing in Kotlin

var number = 100
println("数量有${
      
      number}")
// 换种简洁的写法
var number = 100
println("数量有$number")
// 如果不想字符串被转义可以使用\$
var number = 100
println("数量有\$number")

Thirteen, string comparison

How to write Java

String s1 = "text";
String s2 = "text";
if (s1.equals(s2)) {
    
    
    
}

Kotlin's writing method (Kotlin optimizes the writing method of string comparison, and other types of object comparison still need to use the equals method)

var s1 = "text"
var s2 = "text"
if (s1 == s2) {
    
    

}

14. Array

How to write Java

int[] array1 = {
    
    1, 2, 3};
float[] array2 = {
    
    1f, 2f, 3f};
String[] array3 = {
    
    "1", "2", "3"};

Writing in Kotlin

val array1 = intArrayOf(1, 2, 3)
val array2 = floatArrayOf(1f, 2f, 3f)
val array3 = arrayListOf("1", "2", "3")

15. Cycle

How to write Java

String[] array = {
    
    "1", "2", "3"};

for (int i = 0; i < array.length; i++) {
    
    
    System.out.println(array[i]);
}

Writing in Kotlin

val array = arrayListOf("1", "2", "3")

for (i in array.indices) {
    
    
    println(array[i])
}

16. Corner mark cycle

How to write Java

String[] array = {
    
    "1", "2", "3"};

for (int i = 1; i < array.length; i++) {
    
    
    System.out.println(array[i]);
}

Kotlin's way of writing (this way of writing is called interval in Kotlin)

val array = arrayListOf("1", "2", "3")

for (i in IntRange(1, array.size - 1)) {
    
    
    println(array[i])
}
// 换种更简洁的写法
val array = arrayListOf("1", "2", "3")

for (i in 1..array.size - 1) {
    
    
    println(array[i])
}
// 编译器提示要我们换种写法
val array = arrayListOf("1", "2", "3")

for (i in 1 until array.size) {
    
    
    println(array[i])
}

Seventeen, advanced cycle

How to write Java

String[] array = {
    
    "1", "2", "3"};

for (String text : array) {
    
    
    System.out.println(text);
}

Writing in Kotlin

val array = arrayListOf("1", "2", "3")

for (text in array) {
    
    
    println(text)
}

18. Judge

How to write Java

int count = 1;

switch (count) {
    
    
    case 0:
        System.out.println(count);
        break;
    case 1:
    case 2:
        System.out.println(count);
        break;
    default:
        System.out.println(count);
        break;
}
在这里插入代码片

Writing in Kotlin

var count = 1

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

// 换种更简洁的写法
when (count) {
    
    
    0 -> println(count)
    in 1..2 -> println(count)
    else -> println(count)
}

19. Constructor

How to write Java

public class MyView extends View {
    
    

    public MyView(Context context) {
    
    
        this(context, null);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
    
    
        this(context, attrs, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
        super(context, attrs, defStyleAttr);
    }
}

Writing in Kotlin

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) {
    
    

    }
}
// 换种更简洁的写法
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)
}
// 只有一种构造函数的还可以这样写
class MyView(context: Context?) : View(context) {
    
    

}

Twenty, class creation

How to write Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
}
Person person = new Person("Zhangsan", 100);
person.setName("ZS");
person.setAge(18);
System.out.println("name: " + person.getName() + ", age: " + person.getAge());

Kotlin's way of writing (if you don't want to expose the set method of member variables, you can change var to val)

class Person {
    
    

    var name : String? = null
    get() = field
    set(value) {
    
    field = value}

    var age : Int = 0
    get() = field
    set(value) {
    
    field = value}
}
// 换种更简洁的写法
class Person(var name : String, var age : Int)
var person = Person("Zhangsan", 100)
person.name = "ZS"
person.age = 18
println("name: {
     
     $person.name}, age: {
     
     $person.age}")

Twenty-one, privatization set method

How to write Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    private void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    private void setAge(int age) {
    
    
        this.age = age;
    }
}

Writing in Kotlin

class Person {
    
    

    var name : String? = null
    private set

    var age : Int = 0
    private set
}

Twenty-two, privatization get method

How to write Java

public class Person {
    
    

    String name;
    int age;

    public Person(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    private String getName() {
    
    
        return name;
    }

    private void setName(String name) {
    
    
        this.name = name;
    }

    private int getAge() {
    
    
        return age;
    }

    private void setAge(int age) {
    
    
        this.age = age;
    }
}

Writing in Kotlin

class Person {
    
    

    private var name : String? = null

    private var age : Int = 0
}

23. Enumeration

How to write Java

enum Sex {
    
    

    MAN(true), WOMAN(false);

    Sex(boolean isMan) {
    
    }
}

Writing in Kotlin

enum class Sex (var isMan: Boolean) {
    
    

    MAN(true), WOMAN(false)
}

24. Interface

How to write Java

public interface Callback {
    
    
    void onSuccess();
    void onFail();
}

Kotlin writing method (Kotlin interface method can be implemented by yourself, so it will not be demonstrated here)

interface Callback {
    
    
    fun onSuccess()
    fun onFail()
}

25. Anonymous inner class

How to write Java

new Callback() {
    
    

    @Override
    public void onSuccess() {
    
    
        
    }

    @Override
    public void onFail() {
    
    

    }
};

Writing in Kotlin

object:Callback {
    
    

    override fun onSuccess() {
    
    
        
    }

    override fun onFail() {
    
    
        
    }
}

Twenty-six, inner class

How to write Java

public class MainActivity extends AppCompatActivity {
    
    

    public class MyTask {
    
    

    }
}

Writing in Kotlin

class MainActivity : AppCompatActivity() {
    
    

    inner class MyTask {
    
    
        
    }
}

Twenty-seven, the inner class accesses the variable with the same name as the outer class

How to write Java

String name = "Zhangsan";

public class MyTask {
    
    

    String name = "ZS";

    public void show() {
    
    
        System.out.println(name + "---" + MainActivity.this.name);
    }
}

Writing in Kotlin

var name = "Zhangsan"

inner class MyTask {
    
    

    var name = "ZS"

    fun show() {
    
    
        println(name + "---" + this@MainActivity.name)
    }
}

Twenty-eight, abstract class

How to write Java

public abstract class BaseActivity extends AppCompatActivity implements Runnable {
    
    

    abstract void init();
}

Writing in Kotlin

abstract class BaseActivity : AppCompatActivity(), Runnable {
    
    

    abstract fun init()
}

Twenty-nine, static variables and methods

How to write Java

public class ToastUtils {

    public static Toast sToast;

    public static void show() {
        sToast.show();
    }
}

Kotlin's way of writing (in Kotlin, this way is called companion object)

companion object ToastUtils {
    
    

    var sToast : Toast? = null

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

Thirty, variable parameters

How to write Java

public int add(int... array) {
    
    
    int count = 0;
    for (int i : array) {
    
    
        count += i;
    }
    return count;
}

Writing in Kotlin

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

Thirty-one, generic

How to write Java

public class Bean<T extends String> {
    
    

    T data;
    public Bean(T t) {
    
    
        this.data = t;
    }
}
Bean<String> bean = new Bean<>("666666");

Writing in Kotlin

class Bean<T : Comparable<String>>(t: T) {
    
    
    var data = t
}
var bean = Bean<String>("666666")
// 换种更简洁的写法
var bean = Bean("666666")

Thirty-two, constructing code blocks

How to write Java

public class MainActivity extends AppCompatActivity {
    
    

    int number;

    {
    
    
        number = 1;
    }
}

Writing in Kotlin

class MainActivity : AppCompatActivity() {
    
    

    var number = 0

    init {
    
    
        number = 1
    }
}

Thirty-three, static code block

How to write Java

public class MainActivity extends AppCompatActivity {
    
    

    static int number;

    static {
    
    
        number = 1;
    }
}

Writing in Kotlin

class MainActivity : AppCompatActivity() {
    
    

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

Thirty-five, method code block

How to write Java

void test(){
    
    
    {
    
    
        int a = 1;
    }
}

Writing in Kotlin

fun test() {
    
    
    run {
    
    
        var a =1
    }
}

Thirty-six, visible modifier

How to write in Java (default is default)

Modifier effect
public All classes are visible
protected subclasses visible
default Classes under the same package are visible
private Only visible to own class

Kotlin way of writing (default is public)

Modifier effect
public All classes are visible
internal The classes under the same Module are visible
protected subclasses visible
private Only visible to own class

Thirty-seven, no need to findViewById

defined in the layout

<TextView
    android:id="@+id/tv_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!" />

Set the text of TextView directly

tv_content.text = "改变文本"

Thirty-eight, Lambda

Although Lambda expression has been added in Java JDK, it has not been popularized. Now it is a good choice to use Kotlin

tv_content.setOnClickListener(View.OnClickListener(

    fun(v : View) {
    
    
        v.visibility = View.GONE
    }
))

Simplification is now possible with Lambda expressions

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

Thirty-nine, function variables

Functions can be passed as variables in Kotlin syntax

var result = fun(number1 : Int, number2 : Int) : Int {
    
    
    return number1 + number2
}

use this function variable

println(result(1, 2))

40. Air safety

Java does not force us to deal with empty objects, so it often leads to the appearance of NullPointerException null pointers. Now Kotlin limits the empty objects, and we must deal with whether the object is empty at compile time, otherwise the compilation will not pass.

In the case of non-null objects, you can use this object directly

fun getText() : String {
    
    
    return "text"
}
val text = getText()
print(text.length)

In the case of an object that is nullable, it is necessary to determine whether the object is empty

fun getText() : String? {
    
    
    return null
}
val text = getText()
if (text != null) {
    
    
    print(text.length)
}
// 如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用
val text = getText()
print(text!!.length)
// 还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null
val text = getText()
print(text?.length)

41. The method supports adding default parameters

On Java, we may have multiple overloads to extend a method

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

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

public void toast(Context context, String text, int time) {
    
    
    Toast.makeText(context, text, time).show();
}
toast("弹个窗");
toast(this, "弹个窗");
toast(this, "弹个窗", Toast.LENGTH_LONG);

But on Kotlin, we don't need to overload, we can directly define the default value of the parameter directly on the method

fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
    
    
    Toast.makeText(context, text, time).show()
}
toast(text = "弹个窗")
toast(this, "弹个窗")
toast(this, "弹个窗", Toast.LENGTH_LONG)

Forty-two, class method extension

You can extend the method of the original class without inheritance, such as extending the method of the String class

fun String.handle() : String {
    
    
    return this + "Zhangsan"
}
// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用
print("ZS = ".handle())
ZS = ZhangSan

Forty-three, operator overloading

Using operators in Kotlin will eventually call the corresponding methods of the object. We can make this object support operators by rewriting these methods

operator call method
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
operator call method
a++ a.inc()
a– a.dec()
operator call method
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b), a.mod(b) (deprecated)
a…b a.rangeTo(b)
operator call method
a in b b.contains(a)
a !in b !b.contains(a)
operator call method
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, …, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, …, i_n] = b a.set(i_1, …, i_n, b)
operator call method
a() a.invoke()
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, …, i_n) a.invoke(i_1, …, i_n)
operator call method
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b), a.modAssign(b) (deprecated)
operator call method
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))
operator call method
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

四十五、扩展函数

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

1、let 函数

在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式

一般写法

fun main() {
    
    
    val text = "CurvedBowZhang"
    println(text.length)
    val result = 1000
    println(result)
}

let 写法

fun main() {
    
    
    val result = "CurvedBowZhang".let {
    
    
        println(it.length)
        1000
    }
    println(result)
}

最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理

mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
mVideoPlayer?.let {
    
    
   it.setVideoView(activity.course_video_view)
   it.setControllerView(activity.course_video_controller_view)
   it.setCurtainView(activity.course_video_curtain_view)
}

又或者是需要去明确一个变量所处特定的作用域范围内可以使用

2、with 函数

前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

定义 Person 类

class Person(var name : String, var age : Int)

一般写法

fun main() {
    
    
    var person = Person("CurvedBowZhang", 100)
    println(person.name + person.age)
    var result = 1000
    println(result)
}

with 写法

fun main() {
    
    
    var result = with(Person("CurvedBowZhang", 100)) {
    
    
        println(name + age)
        1000
    }
    println(result)
}

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于 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}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    with(item){
    
    
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

3、run 函数

实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

一般写法

var person = Person("CurvedBowZhang", 100)
println(person.name + "+" + person.age)
var result = 1000
println(result)

run 写法

var person = Person("CurvedBowZhang", 100)
var result = person.run {
    
    
    println("$name + $age")
    1000
}
println(result)

适用于let,with函数任何场景。
因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法;
另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    holder.nameView.text = "姓名:${
      
      item.name}"
    holder.ageView.text = "年龄:${
      
      item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
    val item = getItem(position)?: return
    item?.run {
    
    
        holder.nameView.text = "姓名:$name"
        holder.ageView.text = "年龄:$age"
    }
}

4、apply 函数

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身

一般写法

val person = Person("CurvedBowZhang", 100)
person.name = "ZJX"
person.age = 50

apply 写法

val person = Person("CurvedBowZhang", 100).apply {
    
    
    name = "ZJX"
    age = 50
}

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。
apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到

mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0

使用 apply 函数后的代码是这样的

mRootView = View.inflate(activity, R.layout.example_view, null).apply {
    
    
   tv_cancel.paint.isFakeBoldText = true
   tv_confirm.paint.isFakeBoldText = true
   seek_bar.max = 10
   seek_bar.progress = 0
}

多层级判空问题

if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
    
    
    return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
    
    
    renderAnalysis();
    return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
    
    
    fetchQuestionData();
    return;
}

kotlin的apply函数优化

mSectionMetaData?.apply {
    
    

    //mSectionMetaData不为空的时候操作mSectionMetaData

}?.questionnaire?.apply {
    
    

    //questionnaire不为空的时候操作questionnaire

}?.section?.apply {
    
    

    //section不为空的时候操作section

}?.sectionArticle?.apply {
    
    

    //sectionArticle不为空的时候操作sectionArticle

}

5、also 函数

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

fun main() {
    
    
    val result = "CurvedBowZhang".let {
    
    
        println(it.length)
        1000
    }
    println(result) // 打印:1000
}
fun main() {
    
    
    val result = "CurvedBowZhang".also {
    
    
        println(it.length)
    }
    println(result) // 打印:CurvedBowZhang
}

适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

6、小结

通过以上几种函数的介绍,可以很方便优化 Kotlin 中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景也有相同的地方,比如:run函数就是let和with的结合体。

一张图说明:
在这里插入图片描述

四十六、协程

子任务协作运行,优雅的处理异步问题解决方案

协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能

在当前 app module 中配置环境和依赖(因为现在协程在 Kotlin 中是实验性的)

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
  • runBlocking

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 外面的代码

  • launch

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: 测试延迟结束
  • async

async :异步。用代码测试

测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),和 runBlocking 最不同的是 launch 没有执行顺序这个概念

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 和 launch 还是有区别的,async 可以有返回值,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用

  • 线程调度

协程也有类似 RxJava 的线程调度,先用 launch 试验一下

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(CommonPool) {
    
     // 同学们,敲重点
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
18:00:23.243 System.out: 测试是否为主线程 true
18:00:23.244 System.out: 测试开始
18:00:23.246 System.out: 测试结束
18:00:23.246 System.out: 测试是否为主线程 false
18:00:23.247 System.out: 测试延迟开始
18:00:43.256 System.out: 测试延迟结束

Q:这个跟刚刚的代码有什么不一样吗?

A:当然不一样,假如一个网络请求框架维护了一个线程池,一个图片加载框架也维护了一个线程池…,你会发现其实这样不好的地方在于,这些线程池里面的线程没有被重复利用,于是乎协程主动维护了一个公共的线程池 CommonPool,很好的解决了这个问题

Q:还有刚刚不是说能线程调度吗?为什么还是在子线程运行?

A:因为我刚刚只用了 CommonPool 这个关键字,再介绍另一个关键字 UI,光听名字就知道是啥了

println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(UI) {
    
    
    println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
    println("测试延迟开始")
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
18:07:20.181 System.out: 测试是否为主线程 true
18:07:20.181 System.out: 测试开始
18:07:20.186 System.out: 测试结束
18:07:20.192 System.out: 测试是否为主线程 true
18:07:20.192 System.out: 测试延迟开始
18:07:40.214 System.out: 测试延迟结束

总结

以上这些仅仅只是 Kotlin 的一些基本使用,还有很多神奇之处和高级用法,帮助我们更加优雅地实现功能,后续我会继续补充在后面,也方便自己开发查阅。

Guess you like

Origin blog.csdn.net/T01151018/article/details/130506726