Kotlin 初体验

5,032 阅读7分钟

前言

Kotlin 是 Android 平台第一开发语言,Kotlin 通过他自身的编译器会把代码编译成 Class 文件。

拥有很多现代化语言的高级特性如:高阶函数、Lambda 表达式 、自定义扩展函数等,且极大的增加了开发效率。 几乎杜绝了空指针异常和 Java 百分之百的兼容性且支持无缝调用 Java 的第三方库,继承 Java 的生态。

1.变量

// final int i = 0; // java
// val i: Int = 0 // kotlin

// int n = 0; // java
// var n: Int = 0 // kotlin

val 不可变: 对应 Java 中 final 修饰的变量

var 可变: 对应 Java 中非 final 修饰的变量

// Kotlin 声明变量语法: 变量类型 变量名:数据类型(可选)= 默认值
// var n: Int = 0 // kotlin
// var n = 0 // kotlin

这两行代码的意义是一样的,属于 Kotlin 的一种特性也就是类型自动推导机制

Kotlin 数据类型: 抛弃了 Java 中的基本数据类型,使用对象数据类型。Int 是一个类,拥有自己的方法和继承结构

2.方法

1.png

Kotlin 函数: fun 定义函数关键字,plus 是函数名(可以是张三或李四),大括号里的就是参数,格式为“参数名:参数类型 = 默认值”,参数默认值为可选,参数括号后面是返回值,用于声明当前函数返回什么类型,可选。

// 函数只有一行代码,可以省略大括号,并且 return 也省略了, = 就代表着返回值
fun plus(number: Int = 5, number2: Int = 6, number3: Int): Int =
     number + number2 + number3

// 因为类型自动推导机制的存在,所以可以不用显式地声明返回类型
fun plus(number: Int = 5, number2: Int = 6, number3: Int) = number + number2 + number3

3.条件语句

if 语句: Kotlin 的 if 语句 和 Java 中 if 语句几乎没有区别

// java
int value = 6;
int value2 = 10;
// java 中的三元表达式
int max = value > value2 ? value : value2;

// kotlin
val value = 6
val value2 = 10
// 通过 if 语句实现类似 java 中的三元表达式的效果
val max = if (value > value2) value else value2

when 语句: Kotlin 的 when 语句类似 Java 中 switch 语句,相较于 switch 要强大很多,switch 只支持整型或短与整型的变量作为条件,JDK1.7 以后才增加字符串的支持。

var name = "chao"
// when 支持传入任意类型的参数
val score = when (name) {
    "chao" -> 44
    "dong" -> 55
    else -> 0
}

var num = 80
// when 支持不带参数的用法,适用于处理某些具体条件
val score = when {
    num > 60 -> "及格"
    num > 90 -> "优秀"
    else -> "要努力了"
}

when 结构体条件格式:匹配值 -> { 执行逻辑 },只有一行代码括号可以省略

4.循环语句

while 循环: Kotlin 中 while 循环和 Java 中无区别

for-in 循环: Kotlin 中舍弃了 for-i 而是增强了 for-each,变成 for-in。

// 创建一个区间,创建了一个0到10的区间
val nums = 0..10
// 遍历区间
for (i in nums) {
    print(i)
}

输出 012345678910

// 创建一个降序区间
val nums = 10 downTo 0
for (i in nums) {
    print(i)
}

输出 109876543210

集合初始化和遍历方式

// 创建一个 ArrayList
val arrayList = ArrayList<String>()
// 创建不可变数组 ,无法新增元素
val list = listOf("test", "test2”)
// 创建可变数组 ,可以新增元素
val mutableList = mutableListOf<Person>(Person("test"), Person("test2”))
// 遍历数组
for (item in mutableList) {
    println(item)
}

// 创建一个 HashMap
val hashMap = HashMap<String, String>()
// 创建不可变 Map ,无法新增元素
val map = mapOf("apple" to 1, "banana" to 2)
// 创建可变 Map ,可以新增元素
val mutableMap = mutableMapOf("apple" to 1, "banana" to 2)
// 获取 value
val number = mutableMap["apple"]
// 遍历 Map
for ((key, value) in mutableMap) {
    println("key is $key value is $value")
}

5.类与对象

open 关键字: 表示当前类可被继承,在 Effective Java 中明确提到,一个类如果不是专门为继承而设计,那么应该主动加上 final 声明,防止它可以被继承。

open class Person(var name: String)

// 括号里面的就是 kotlin 中的主构造函数,没有方法体
class Programmer(name: String, val isBald: Boolean) : Person(name) {

    // 如果想在主构造函数中加逻辑,那么直接写到 init 结构体里面
    init {
         println("name is $name")
         println("bald is $isBald")
    }

    // 次级构造函数
    // 当一个类拥有主构造函数和次构造函数时,所有次构造函数都必须调用主构造函数
    constructor(name: String) : this(name, false)
}

次构造函数: 关键字 constructor,当一个类拥有主构造函数和次构造函数时,所有次构造函数都必须调用主构造函数,一个类如果没有主构造函数,次构造函数只能直接调用父类的构造函数,Kotlin 提供参数默认值的功能,基本可以替代次构造函数

Kotlin 继承 Java 类,@JvmOverloads 注解 有兴趣可以了解下

// 无主构造函数
class TestFrameLayout : FrameLayout {

    // 声明的次级构造函数,由于没有主构造函数,次构造函数只能直接调用父类的构造函数
    @JvmOverloads
    public constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : super(context, attrs, defStyleAttr) {
        println()
    }
}

Kotlin 数据类: 关键字 data,里面分装了 equals()、hashCode()、toString() 等一系列方法

// 数据类:里面分装了 equals()、hashCode()、toString() 等一系列方法
// Parcelable 方式序列化数据
@Parcelize
data class User(val name: String): Parcelable

Kotlin 单例类: 关键字 object,构建一个单例类

// 单例类
object Singleton{
    fun  singletonTest(){
        println("SingletonTest")
    }
}

Mac: tools -> kotlin -> show kotlin bytecoed 然后点击 decompile 反编译 kotlin 代码

2.png

object 关键字对应的 Java 代码

6.空指针检测

空指针异常是 Android 最常见且奔溃率最高的异常。在 Java 中空指针不受编程语言检查的运行时异常。

// Java
public static class Example {
    public String test;
}

public static class SimplesJava {
    public void run(Example example) {
        System.out.println(example.test);
    }
}

// Kotlin
class Example {
    var test: String = ""
}

class SimplesKotlin {
    fun run(example: Example) {
        println(example.test)
    }
}

// 调用 Java 代码,可以编译通过但是会触发 NullPointerException
JavaTest.SimplesJava().run(null)

// 调用 Kotlin 代码,无法编译通过
// Kotlin 特性:编译时判空检查机制去杜绝大部分空指针异常
SimplesKotlin().run(null)

Kotlin 默认所有参数和变量都不为空,但是某些业务场景下需要声明变量为空怎么办?Kotlin 提供了一套可为空的类型系统,注意一样需要在编译期解决所有潜在的空指针异常,否则无法编译通过

接着修改一下代码

class Example {
    var test: String = ""
}

class SimplesKotlin {
    // 可空类型就是在类名的后面加一个 ?
    // Int 表示不可为空的整型,Int?表示可为空的整型。
    // String 表示不可为空的字符串,String?表示可为空的字符串。
    fun run(example: Example?) {
        if (example != null) {
            println(example.test)
        }
    }
}

// 现在可以编译通过了
SimplesKotlin().run(null)

空指针辅助工具

?. 操作符: 当对象不为空时正常调用

// 1
if (example != null) {
    println(example.test)
}

// 2
println(example?.test)

// 代码 2 通过 ?. 操作符简化代码 1。

?: 操作符: 如果左边的结果不为空返回左边的结果,否则返回右边的

// 1
val value = if (num1 != null) {
    num1
} else {
    num2
}

// 2
val value = num1 ?: num2

// 代码 2 通过 ?: 操作符简化代码 1。

?. 操作符 和 ?: 操作符组合使用

// 1
fun getTextLength(text: String?): Int {
    if (text != null) {
        return text.length
    }
    return 0
}

// 2
fun getTextLength(text: String?) = text?.length ?: 0

// 代码 2 通过 ?. 操作符 和 ?: 操作符组合使用简化代码 1。
// 代码 2 :当 text 为空时,那么 text?.length 会返回一个 null 值,再借助 ?: 让它返回 0 

!! 非空断言: 我能确定当前变量不为空想要强行通过编译,如果出现了问题可以直接抛出空指针异常

fun getTextLength(text: String?) = text!!.length

空指针辅助工具解决了全局变量的判空问题,因为全局变量在多线程的情况下可能出现线程安全问题。

var simplesKotlin: SimplesKotlin? = null

// 1
fun main() {
    // 这个是没办法编译通过的,因为在多线程的情况下会出现空指针异常
    if (simplesKotlin != null) {
        simplesKotlin.run(null)
    }
}

// 2
fun main() {
    simplesKotlin?.run(null)
}

代码 2 如何实现多线程的线程安全?很简单反编译一下,原理很简单就是通过局部变量保存指针

public final class TtKt {
   @Nullable
   private static SimplesKotlin simplesKotlin;

   @Nullable
   public static final SimplesKotlin getSimplesKotlin() {
      return simplesKotlin;
   }

   public static final void setSimplesKotlin(@Nullable SimplesKotlin var0) {
      simplesKotlin = var0;
   }

   public static final void main() {
      // 在这里通过一个局部变量存储 simplesKotlin 的指针
      // 其他线程把 simplesKotlin 重置为 null 依然不会对 var10000 产生影响
      SimplesKotlin var10000 = simplesKotlin;
      if (var10000 != null) {
         var10000.run((Example)null);
      }

   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

字符串内敛表达式

val name = "dong.chao"
// 可以在字符串中嵌入 ${} 语法结构的表达式,如果表达式只是一个变量就可以省略 {}
val str = "name is $name ${SimplesKotlin().run(null)}"

总结

主要是讲解一些 Kotlin 比较基础的一些东西,总体来说比较简单,后期会出 Kotlin 高级使用技巧。