【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客

目录

初识 Kotlin

历史

工作原理

第一个Hello World!

Kotlin 语法

变量

基本数据类型

函数

== 和 ===

选择控制(if、when)

if

when

循环语句

类和对象

创建和使用

继承

构造

主构造

次构造

接口

定义

实现

权限修饰符

数据类(实体类)和单例类

数据类

单例类

集合

List

Set

Map

Lambda 的使用

基本认识

list.maxByOrNull

list.map、list.filter、list.any、list.all

空指针检查机制

辅助判空工具

?.

?:

!!

let 函数

内嵌表达式

函数参数默认值


初识 Kotlin


历史

Kotlin由JetBrains公司开发设计,2011年公布第一版,2012年开源。

2016年发布1.0正式版,并且JetBrains在IDEA加入对Kotlin的支持,安卓自此又有新的选择。

2019年谷歌宣布Kotlin成为安卓第一开发语言,安卓程序员由java转Kotlin已经迫在眉睫。

工作原理

Java 虚拟机只认识 class 文件,并不关心是由什么文件编译来的, 因此当我们创造一个自己的语法规则时,再做一个对应的编译器,就可以让我们的语言跑在 Java 的虚拟机上。Kotlin 就是这个原理,运行前会先编译成 class,在给 Java 虚拟机运行。

第一个Hello World!

创建项目只需要注意以下几点即可(这里使用 Gradle 构建项目)

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第1张图片

修改配置文件中的 JDK 版本(这里我使用的是 JDK8)

如下代码(Kotlin 以 main 方法作为程序的入口)

fun main() {
    println("hello world!")
}

运行结果如下

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第2张图片

接下来,进入语法的学习

Kotlin 语法


变量

var 表示可变变量

val 表示不可变变量

a)由于 Kotlin 存在类型推导机制,因此不用声明具体的数据类型。当然也可以手动指定数据类型,如下代码

    var a = 1  //可变
    val b = 2  //不可变
    var c: Int = 1 //指定数据类型(Kotlin有类型推导机制,这里可以不用指明)

他们的本质区别就类似 Java 中如下代码

    private static int a = 1;
    private static final int b = 2;
    private static int c = 3;

 Kotlin 这样设计是为了防止非 final 类型的滥用,也就是说,如果一个变量永远不会被修改,就有必要给他加上 final,让其他人看到代码更好理解。

Ps:建议写代码时,可以先使用 val,如果真的需要修改,再改为 var

b)如何查看 Kotlin 对应的 Java 代码?如下

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第3张图片

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第4张图片

基本数据类型

Kotlin 不存在基本类型,全部都是对象类型

Java基本类型 Kotlin对象类型 对象类型说明
int Int 整型
long Long 长整型
short Short 短整型
float Float 单精度浮点型
double Double 双精度浮点型
boolean Boolean 布尔型
char Char 字符型
byte Byte 字节型

函数

无参无返回值

fun add1() {

}

有参有返回值,语法如下:

fun 方法名(参数名: 类型): 返回值类型 { 函数体 }

fun add2(a: Int, b: Int): Int {
    return a + b
}

当函数体只有一行代码时可以直接使用以下方式声明

fun add3(a: Int, b: Int): Int = a + b

Kotlin 存在类型推导,返回值类型也可省略

fun add4(a: Int, b: Int) = a + b

调用的的时候,直接写函数名即可

fun add4(a: Int, b: Int) = a + b


fun main() {
    println(add4(3, 5))
}

== 和 ===

Kotlin 中的 == 等价于 Java 的 equals,比较的是对象里的内容(Kotlin 中只有对象类型,不存在值比较),而 ===  等价于 Java 中 == ,比较的是对象的引用.

选择控制(if、when)

if

Kotlin 中的 if 和 Java 基本上没有区别

例如,实现一个返回最大值的函数,有以下多种写法

fun max1(a: Int, b: Int): Int {
    if(a > b) {
        return a
    }
    return b
}

fun max2(a: Int, b: Int): Int {
    return if(a > b) a else b  //if 也可以直接返回值
}

fun max3(a: Int, b: Int) = if(a > b) a else b

when

a)类似 Java 中的 switch 语句,可以进行选择控制

如下代码

fun whenTest(num: Int) {
    when(num) {
        1 -> println("1")
        2 -> println("2")
        3 -> println("3")
        4 -> println("4")
        else -> println("非法!") //else 可有可无
    }
}

解释:例如当 num 为 2 时,when 就会匹配 num 为 2,执行 println("2"),程序结束.  与 switch 不同的是,这里执行完 num 为 2 逻辑后,不需要 break,自动跳出 when 语句;如果 num 非 1、2、3、4,就会执行 else 逻辑.

c)when 也支持执行代码块

fun whenTest2(name: String) {
    when(name) {
        "1" ->  {
            println("你好1")
            println("你好2")
            println("你好3")
        }
        "2" -> println("我好像作用不大")
        else -> println("非法")
    }
}

b)when 支持参数检查

fun checkNumber(num: Number) {
    when (num) {
        is Int -> println("Int")
        is Double -> println("Double")
        else -> println("others")
    }
}

c)when 也可以不传递参数

fun whenTest3(name: String) {
    when {
        name == "cyk" -> println("男")
        name == "lyj" -> println("女")
        else -> println("emm...")
    }
}

循环语句

Kotlin 有两种循环方式:while 和 for-in

while 和 Java 中的 while 没有区别(因此这里就不再赘述 while 了),而 for-in 则是对 for-each 的加强,舍弃了 for-i 的写法.

对于 for-in 的写法,首先要明确一个 区间 的概念

    val range = 0..10 //代表区间 [0, 10],前闭后闭

a)for-in 需要使用区间

fun test1() {
    val range = 0..10 //前闭后闭
    for(i in range) { //也可以使用 for(i in 0..10)
        print("${i} ")
    }
}

b)0..10 表示双闭区间,如果想使用左闭右开,需要借助 until 关键字

fun test2() {
    for(i in 0 until 10) { //前闭后开 [0, 10)
        print("${i} ")
    }
}

c)上面的代码类似于 i++,Kotlin 也支持跳步

fun test3() {
    for(i in 0 until 10 step 2) {
        print("${i} ")
    }
}

d)以上实现都是升序,Kotlin也可以实现降序循环.

fun test4() {
    for(i in 10 downTo 0) { //前闭后闭
        print("${i} ")
    }
}

for-in 还可以进行集合的遍历,后续再演示.

通过 main 函数依次执行以上函数,如下:

fun main() {
    test1()
    println("----------------------")
    test2()
    println("----------------------")
    test3()
    println("----------------------")
    test4()
    println("----------------------")
}

执行结果如下:

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第5张图片

类和对象

创建和使用

创建一个 Person 类,具有 name 和 age 属性,showInfo 方法(打印信息),代码如下

class Person {
    var name = ""
    var age = 0
    fun showInfo() {
        println("name: ${name}, age: ${age}")
    }
}

对象的创建和使用如下

    val person = Person()
    person.name = "cyk"
    person.age = 20
    person.showInfo()

继承

声明 Person 类,声明 Student 类并继承 Person 类

open class Person {
    var name = ""
    var age = 0
    fun showInfo() {
        println("name: ${name}, age: ${age}")
    }
}

class Student : Person() { //被继承的类需要使用 open 修饰(需要在 Person 类前添加 open)
    var num = 1
    var grade = 0
    fun study() {
        println("student: ${name} do homework")
    }
}

注意:Person 类默认情况下不能被继承的,因为默认情况下类似于 Java 中的 final 修饰的,因此。这里需要使用 open 关键字才可以解除 final。(否则 IDEA 自动语法检测报错)

构造

主构造

在 Kotlin 中,分为主构造和次构造。

a)主构造直接写在类后面即可,如下代码

//主构造(直接写在类后面)
class Student(val num: Int, val grade: String) {
}

创建方式如下

    var student = Student(1, "三年二班")

b)当 Person 和 Student 都有主构造,并且 Student 继承了 Person 类时,就需要修改 Student 的主构造,附带上父类的参数(此时不需要指定 var 或 val),如下代码

open class Person(val name: String, val age: Int) {
}

class Student(val num: Int, val grade: String, name: String, age: Int):
Person(name, age) {
}

fun main() {
    //创建 Student 对象
    var student = Student(1, "三年二班", "cyk", 20)
}

c)如果在构造时需要进行一些特殊处理怎么办?Kotlin 中提供了 init 结构体,主构造的逻辑可以在 init 中进行处理,如下代码

class Student(val num: Int, val grade: String, name: String, age: Int):
Person(name, age) {
    init {
        println("对主构造中的参数进行了一些特殊处理")
        println("...")
    }
}

如果一个类想要有多种构造方法,该怎么做?这就需要用到次构造

次构造

a)constructor 中只需要指定创建该对象时需要的参数(也就是说,无参构造无需指定任何参数),this 中就表明了需要什么样的参数,代码如下

class Student(val num: Int, val grade: String, name: String, age: Int):
Person(name, age) {
    //两个参数的构造
    constructor(name: String, age: Int) : this(0, "", name, age){

    }
    //无参构造
    constructor(): this(0, "", "", 0) {

    }
}

通过以上构造,就有三种创建 Student 对象的方式,如下

    //创建 Student 对象
    var student1 = Student(1, "三年二班", "cyk", 20)
    var student2 = Student("cyk", 20)
    var student3 = Student()

 b)如果类不要主构造,那么继承类也无需通过 () 的方式初始化参数,子类次构造可以通过 super 来初始化父类的构造器

class Student: Person {
    constructor(name: String, age: Int, grade: String) : super(name, age){

    }
}

创建对象如下

    //创建 Student 对象
    Student("cyk", 20, "三年二班")

接口

定义

和 Java 基本没什么区别

interface Study {

    fun study()
    fun sleep()
    fun doHomework()

}

Kotlin 支持接口的方法有默认实现(JDK1.8以后支持此功能),如果有默认实现,则继承类可以重写该方法,也可以不重写,若重写,以重写为主,否则执行默认实现.

interface Study {
    fun study() {
        println("学习ing ~")
    }
    fun doHomework()
}

class Student(val name: String, val age: Int): Study, Other { //多个接口只需要使用逗号隔开

//    override fun study() { //可以不重写~ 若重写,则执行此方法
//        println("学习!")
//    }

    override fun doHomework() {
        TODO("Not yet implemented")
    }

    override fun sleep() {
        TODO("Not yet implemented")
    }

}

实现

Kotlin 和 Java 一样,支持一个类实现多个接口,需要实现所有方法(若接口的方法有默认实现,则可以不重写该方法)

class Student(val name: String, val age: Int): Study, Other { //多个接口只需要使用逗号隔开

    override fun study() {
        TODO("Not yet implemented") //TODO 表明该方法未被实现,因此实现该方法时,需要将这一行删除
    }

    override fun doHomework() {
        TODO("Not yet implemented")
    }

    override fun sleep() {
        TODO("Not yet implemented")
    }

}

权限修饰符

Java 和 Kotlin 的异同点.

需要注意的是,Kotlin 中移除了 default,引入了 internal 修饰.

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认)
private 当前类可见 当前类可见
protected 当前类,子类,同包下类可见 当前类,子类可见
default 同包下类可见(默认)
internal 同模块下的类可见

使用方式和 Java 没什么区别

//类上
public open class Solution(val name: String, val number: Int) {

    //变量上
    private val key = 1

    //方法上
    private fun test() {

    }

}

数据类(实体类)和单例类

数据类

数据类则只处理数据相关(实体类),与Java Bean类似,通常需要实现其getsethashCodeequalstoString等方法

例如一个用户实体类,Java 代码如下:

public class UserJava {

    private Integer id;
    private String username;
    private String password;

    public UserJava() {
    }

    public UserJava(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserJava userJava = (UserJava) o;
        return Objects.equals(id, userJava.id) && Objects.equals(username, userJava.username) && Objects.equals(password, userJava.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password);
    }

    @Override
    public String toString() {
        return "UserJava{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

}

而 Kotlin 实现以上实体类,只需要一个新建一个 kt 文件,选择创建 Data Class 类型,一行代码即可搞定(虽然一行可以搞定,但是建议还是分行写,可读性高),如下

data class UserKotlin(
    val id: Int,
    val username: String,
    val password: String
)

若无data关键字,上述方法(hashCodeequalstoString)无法正常运行.

单例类

Java 最广泛使用的单例如下

public class Singleton {

    private static final Singleton singleton = new Singleton();

    public static Singleton getSingleton() {
        return singleton;
    }

    private Singleton() {

    }

    /**
     * 测试方法
     */
    public void test() {

    }

}

而 Kotlin 中实现单例模式只需要 object 修饰即可,如下代码

object Singleton {
    fun test() {
        println("hello")
    }
}

 使用如下

fun main() {
    Singleton.test() //等同于 Java 中 Singleton.getSingleton.test
}

集合

List

fun listTest() {
    //常规创建
    //list1 类型:ArrayList
    var list1 = ArrayList()
    list1.add(1)
    list1.add(2)
    list1.add(3)

    //创建不可变类型,创建后不可进行增加和删除操作,只能进行查询
    //list2 类型: List
    val list2 = listOf(1, 2, 3)

    //创建可变类型,创建后可以继续增删改查
    //list3 类型: MutableList
    var mutableListOf = mutableListOf()
    mutableListOf.add(1)
    mutableListOf.add(2)

    for(value in list1) {
        print("$value ")
    }
}

Set

fun setTest() {
    //常规创建
    //set1 类型: HashSet
    var set1 = HashSet()
    set1.add(1)
    set1.add(2)
    set1.add(3)

    //创建不可变类型,创建后不可进行增加和删除操作,只能进行查询
    //set2 类型: Set
    var set2 = setOf(1,2,3)

    //创建可变类型,创建后可以继续增删改查
    //set3 类型: MutableList
    var set3 = mutableSetOf()
    set3.add(1)

    for(value in set1) {
        print("$value ")
    }
}

Map

fun mapTest() {
    //常规创建
    //map1 类型: HashMap
    var map1 = HashMap()
    map1.put("name", "cyk")
    map1.put("age", "20")
    //Kotlin 中 map 支持下表的赋值和访问
    map1["id"] = "1"
    map1["gender"] = "男"

    //创建不可变类型,创建后不可进行增加和删除操作,只能进行查询
    //map2 类型: mapOf
    var map2 = mapOf("name" to "cyk", "age" to "20")

    //创建可变类型,创建后可以继续增删改查
    //set3 类型: MutableList
    var map3 = mutableMapOf()
    map3.put("name", "cyk")
    map3.put("age", "20")
    map3["id"] = "1"
    map3["gender"] = "男"

    for((key, value) in map3) {
        println("$key   $value");
    }

}

Lambda 的使用

基本认识

方法在传递参数时都是普通变量,而Lambda可以传递一段代码

Lambda表达式的语法结构

{参数名1: 参数类型, 参数名2:参数类型 -> 函数体}

list.maxByOrNull

Kotlin 的 list 提供了 maxByOrNull 函数,参数就是一个 lambda 表达式,用来返回当前 list 中 xx 最大的元素,xx 是我们定义的条件,可能为长度,可能是别的.

这里拿长度来举例,例如我有一个 list,现在要找出 list 中长度最大的元素,如下代码

fun test1() {
    //找出 list 中长度最大的元素
    val list = listOf("a", "abc", "abcd", "ab")

    //写法1
    val lambda = {str: String -> str.length}
    var maxStr = list.maxByOrNull(lambda)

    //写法2
    maxStr = list.maxByOrNull {str: String -> str.length}

    //写法3
    maxStr = list.maxByOrNull() {str: String -> str.length}

    //写法4: 基于 Kotlin 的类型推导机制,Lambda 可以省略参数类型
    maxStr = list.maxByOrNull {str -> str.length}

    //写法5: 若 Lambda 只有一个参数,可以使用 it 代替参数名
    maxStr = list.maxByOrNull {it.length}

    println(maxStr)
}

list.map、list.filter、list.any、list.all

    //创建一个 list,后续操作都以此展开
    var list = listOf("a", "ab", "abc", "abcd")

    //map 用于返回一个新的集合,例如将集合中的元素都换成大写
    val mapList = list.map { it.toUpperCase() }

    //filter 过滤,返回一个新集合,例如对集合中的元素进行筛选(筛选出长度大于 3 的元素)
    val filterList = list.filter { it.length > 3 }

    //any 返回 Boolean,用来判断集合中是否有元素满足 Lambda 条件,只要有任意一个满足返回 true,否则返回 false
    val anyList = list.any {it.length > 3} //存在一个字符串长度大于 3,因此返回 true

    //all 返回 Boolean,集合中元素是否全部都满足 Lambda 的条件,全都满足才返回true,有任意一个不满足就返回 false
    val allList = list.all { it.length <= 4 } //所有元素长度都满足字符串长度小于等于 4, 因此返回 true

空指针检查机制

国外统计程序出现最多的异常为空指针异常,Kotlin存在编译时检查系统帮助我们发现空指针异常。

Kotlin把空指针异常的检查提前到了编译期,若空指针则编译期就会崩溃,避免在运行期出现问题,因此在 Kotlin 中,任何变量和参数都不允许为空.

a)参数为空,报错

fun study(study: Study) {
    study.doHomework() //正确
    study.readBook()   //正确
}

fun main() {
    study(null) //报错
    study(Study()) //正确
}

b)如果有需求就是要传入 null,那么可以通过 "?" 来对传入可能为 null 的参数在类型后进行声明

fun study(study: Study?) {
    study.doHomework() //报错
    study.readBook()   //报错
}

fun main() {
    study(null) //正确
    study(Study()) //正确
}

c)?的意思则是当前参数可为空,如果可为空的话,则此对象调用的方法必须要保证对象不为空,上面代码没有保证,则报错,修改如下

fun study(study: Study?) {
    if(study != null) {
        study.doHomework() //正确
        study.readBook()   //正确
    }
}

fun main() {
    study(null) //正确
    study(Study()) //正确
}

辅助判空工具

?.

表示 ?前面对象不为空才执行.后面的方法

fun study(study: Study?) {
    study?.doHomework() 
    study?.readBook()   
}

?:

表示 ?前不为空则返回问号前的值,为空则返回后的值

fun test1(a: Int, b: Int) {
    val c = a ?: b //a 不为空返回 a,为空返回 b
}

!!

如果想要强行通过编译,就需要依靠!!,这时就是程序员来保证安全

fun study(study: Study?) {
    study!!.doHomework()
    study!!.readBook()
}

let 函数

let 是一个函数,提供了函数式 API 接口,会将调用者作为参数传递到 Lambda 表达式,调用之后会立马执行 Lambda 表达式的逻辑.

aaa.let { it ->  // it 就是 aaa(调用者)
    //执行业务逻辑
}

例如,原本函数的逻辑是这样的

fun study(study: Study?) {
    if(study != null) {
        study.doHomework()
        study.readBook()
    }
}

通过 let 则可以改为

fun testLet(study: Study?) {
    //此时通过 ?.就保证了 study 不为空的时候才会执行 let 函数
    study?.let { it ->
        it.doHomework()
        it.readBook()
    }
}

a)好处1:最常用的就是使用 let 函数处理一个可 null 的对象做统一判空处理(例如上述代码).

b)好处2:在有限的范围内引入局部变量提高代码的可读性.

内嵌表达式

以前我们拼接字符串可能是这样的,如下

fun printTest() {
    val name = "cyk"
    val age = 20
    println("name: " + name + ",age: " + age)
}

a)通过 Kotlin 提供的内嵌表达式就不需要拼接了,只需要如下代码

fun printTest() {
    val name = "cyk"
    val age = 20
    println("name: $name, age: $age")
}

b)内敛表达式还支持复杂的操作,语法为 ${表达式}

fun printTest() {
    val name = "cyk"
    val age = 20
    println("name: ${if (2 > 1) "cyk" else "lyj"}, age: $age")
}

函数参数默认值

a)Kotlin 支持函数默认值,如下

fun study(name: String, age: String = "男") {
    println("name: $name, age: $age")
}

fun main() {
    study("cyk")
    study("lyj", "女")
}

运行结果

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第6张图片

b)如果方法的第一个参数设置成默认值,那么传入的第一个实参就会匹配第一个形参,因此就可能出现以下问题.

fun study(age: String = "男", name: String) {
    println("name: $name, age: $age")
}

fun main() {
    study("cyk") //报错
}

你可能会这样想:因为第一个设置了默认值,而我又想让我传入的实参就不匹配设置了默认值的形参了...

如果一定要这么做,Kotlin 也提供了键值对的形式来匹配形参,解决上述问题,如下

fun study(age: String = "男", name: String) {
    println("name: $name, age: $age")
}

fun main() {
    study(name = "cyk") //报错
}

【Kotlin】一款专门为 Java 程序员打造的快速掌握 Kotlin 技术博客_第7张图片

你可能感兴趣的:(Kotlin,kotlin,开发语言,android)