Kotlin基础全教程

Android为啥要从Java转向Kotlin

Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。苹果公司已经在用Swift语言替代Object-C语言,Google也找到了替代Java的语言,也就是JetBrains公司(Android Studio也是用该公司的Intelli J改的)主推的Kotlin。现在将kotlin作为了编写Android的官方语言,而后会有越来越多的项目使用Kotlin。Kotlin有了强有力的亲爸JetBrains公司和Google这个干爸,自然被捧在手心了。

Kotlin在编写代码时有如下优势:

  • 代码简洁高效
  • Android Jetpack 与其他库中的 Kotlin 支持
  • 可与 Java 的一起使用
  • 空指针安全

同样,kotlin支持函数式编程、支持lambda表达式、流式API...,此处略去一万字。总之,Java是上世纪的编程语言了,当你学过了Kotlin之后 ,你会发现,之前写过那么多的代码都是在浪费生命。

语法基础

变量
    var age: Int = 0
    var num = 20
    val TYPE: Int = 100  
    var name: String = ""  

写法形如:var str: String
用var或val声明,前面是参数名,后面是参数的类型,中间用:分割

var即英文“variable”的意思,声明成一个可变变量。val即英文“value”的意思,申明的是一个不可变变量,对应的java里面的final。

kotlin中有优秀的类推导机制,age声明了变量类型,num没有声明变量类型,但是依然可以通过,是由Kotlin自动推导了类型。

常量

const
如果只读属性的值在编译期是已知的,那么可以使用 const 修饰符将其标记为编译期常量,相当于java中的public static final修饰。

    //const val 需与 companion object 搭配使用
    companion object {
        const val VERSION = 1
    }
空指针检查

在Kotlin里,可以用“?”表示可以为空,也可以用“!!”表示不可以为空。
给变量加上?标识,会通告所有使用该变量的地方,必须给出为空的补救措施。

     var info: String? = null

        println(info?.length)  //第一种补救:如果info为null,就不执行后面的.length代码

        println(info!!.length)  //第二种补救:这里如果为null,我自己负责info,会报出空指针,这种处理需慎用

        if (info != null) {   //第三种补救措施,如下这种同java写法
            println(info.length)
        }

      println(info?.length ?: "空数据")  //第四种补救措施,如果真的为null,则改为返回"空数据"

第二个检查方式是let函数,?.当然能解决大部分问题,但是user每使用一次?.相当于都加上了代码if(user != null),实际上在同一个函数中,我们只需要做一次非空判断就行了,这就是我们的let函数的作用,在代码块内容用it代替该对象。

user?.let {
            it.login()
            it.logout()
        }
可见性修饰符

在 Kotlin 中有这四个可见性修饰符:private、 protected、 internal 和 public。 如果没有显式指定修饰符的话,默认可见性是 public。
internal:在本模块内可见

字符串拼接

将变量拼接在字符串之内,用${}来包裹变量即可

    fun StringFormat(title: String) = {
        "这里是拼接字符串${title}"
    }

字符串自动换行:

val content = """
            哈哈哈哈
            呵呵呵呵
            嘿嘿嘿嘿
        """.trimIndent()
值比较、赋值:
        println(name1.equals(name2))  //同java的equals

        println(name1 == name2)  //同equals作用,比较值的相等,这里为true

        println(name1 === name2)  //比较地址是否相等,即比较是否为同一个对象,这里为false

    val name1 = "lili"
    val name2 = "lili"
    println(name1 === name2) //这里返回ture,因为字符串在常量池是复用一份的

将条件判断的结果赋值:

        val num1 = 100
        val num2 = 101
        val max = if (num1 > num2) num1 else num2
when

用when关键字代替java中的switch使用,when作为判断,条件可为任意类型

fun whenUse(obj: Any, type: Int) {
        when(obj) {
            1 ->
                ""
            in 2..5 ->
                ""
            is String ->
                ""
            else ->
                ""
        }
    }

val week = 5
        val info = when(week) {
            1 -> "星期一"
            1 -> "星期二"
            3 -> println()
            4 -> 5
            -1 -> TODO()  //Nothing类型:表示未实现的功能,会抛出异常 public inline fun TODO(): Nothing = throw NotImplementedError()
            else -> ""  //返回String类型,info一定是String类型
        }

//将when的返回值直接使用起来赋值:
        val info2 = when(week) {
            1 -> "星期一"
            2 -> "星期二"
            3 -> println()
            4 -> true
            in 2..5 ->
                ""
            else -> {  //必须要有else
                //else 返回括号,info2就可以是任意类型 Any
            }
        }
Any

java中所有类的父类是Object,而Kotlin中所有类的父类为Any。

object

Kotlin中没有大写的Object了,而是有小写的object,表示单例。

class SingleTon {
    object Holder {
        var instance = SingleTon()
    }
}
is

Kotlin中用is代码java中的instanceOf 来判断类型

 fun charge() {
        var type = ""
        if (type is String) {  //instanceOf

        }
    }
in使用
  • in来检查一个值是否在一个区间内
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'   //检查字符是否为字母
  • 检查list中是否有元素name
    fun inList() {
        val names = listOf("lala", "haha", "yaya")
        if("lili" in names) {

        }
    }
takeIf,takeUnless函数
        user.name.takeIf {
            TextUtils.isEmpty(it)
        }.let {
            print(it)
        }

        user.name.takeUnless {
            !TextUtils.isEmpty(it)
        }.let {
            print(it)
        }

takeIf的闭包返回一个判断结果,如果为false时takeIf函数返回null;takeUnless与takeIf相反,为true时takeUnless函数返回null。

使用场景:只需要单个if分支语句的时候
优点:
可以配合其他作用域函数返回的结果,做出单向判断,保持链式调用
简化写法,逻辑清晰,减少代码量,代码更优雅

init

主构造函数里不能写代码,那我们怎么初始化这个类的代码呢?Kotlin则是提供了初始化模块,基本上就是用init修饰符修饰一个{},在类初始化时执行这段代码。

    class User() {
        init {

        }
    }

数组

创建数组
        var names = arrayOf("小王", "小花", "小红", "小明")

        var ages = arrayOf(22, 19, 18)

        var array = arrayOfNulls<String>(5)   //创建空数组
        array.set(1, "哈哈")   //赋值
数组遍历
    fun arrayForEach() {
        var names = arrayOf("小王", "小花", "小红", "小明")
        for (i in 1..5 step 1) {   //跳过第1步遍历

        }

        for (i in 0..names.size-1) {  //遍历names数组,相当于java里  for (int i=0:i<names.length;i++)

        }

        for (i in names.indices) {  //正序遍历

        }

        for (i in 1 until 10){    //为一个左闭右开的区间,打印1到9
            print("$i")
        }

        for (i in names.size downTo 0) {  //倒序遍历

        }

        for (i in names.reversed()) {  //反转遍历数组

        }

        repeat(10) {  //打印0到9
            print(it)
        }

        names.forEach {  //forEach函数遍历,Kotlin里的集合都自带foreach函数

        }

        names.forEachIndexed { index, s ->   //带position的遍历
            
        }
    }

in的用法:val num = 1..10,表示变量num是一个[1,10]的区间。
step表步长,表示遍历间隔的个数。

类与对象

类构造函数

在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。

class User constructor(name: String, age: Int, id: Int)

class User(name: String, age: Int, id: Int)

操作示例:

class User(val name: String) { //主构造函数
    var age = 0
    var sex = "man"

    /**
     * 有参次构造方法
     */
    constructor(name: String, age: Int, id: Int): this(name) {  //次构造函数,必须去调用主构造

    }
    constructor(name: String, sex: String): this(name) {
        this.sex = sex
    }

    /**
     * 无参次构造方法
     */
    constructor(): this("暂无名字") {

    }

    init {
        println("name is $name")
        println("age is $age")
    }

    fun main() {
        User()   //次构造
        User("小明")  //主构造
        User("小明", 18, 1)  //次构造
    }
}
创建对象

Kotlin中创建对象不再需要关键字new了,直接创建对象:

var user = User()
继承

在Kotlin中的类和方法默认是不可以被继承的,需要加上关键字open,才允许被继承。继承用":"表示,类和接口的继承都用":",类需要加()。

class VipUser : User(), Use {}

open class User {}

interface Use {}
内部类
class OuterClass {

    val outerInfo: String = "outerInfo"

    fun show() {
        InnerClass().show() //外部类可以访问内部类
    }

    //加上inner关键字才是内部类,内部类才能和外部类相互访问,否则不是内部类而是嵌套类
    inner class InnerClass {
        fun show() = println("my outerclass info: " + outerInfo) //内部类也可以访问外部类
    }
}
数据类

即为我们通常使用的bean类,一般在定义数据类的时候,我们需要手动实现这个类的equals()、hashcode()、toString()等方法,这些方法是必要的。但是加上data关键字后,该类会自动生成这些方法。

data class User(var name: String) {

}
单例类

在java中,单例的写法需要自己手动实现,比如用懒汉式还是饿汉式。但是在Kotlin中单例就非常简化了,只需要将class替换成object就可以了。

    object SingleTonClass {

        var params: String? = null

        fun function() {

        }
    }

    SingleTonClass.function()

    SingleTonClass.params

使用单例时不需要再获取instance实例对象了,Kotlin已经在内部帮我们创建了一个单例的实例,直接使用类名调用它的属性和方法。

compaion object(伴生对象)

在java中static表静态变量,kotlin中取消了static,用compaion object来代替它的用法。

class Companionobject {

    //伴生对象的由来,是kotlin没有static静态
    //不管对象创建多少个,companion object只会初始化一次
    companion object {
        val info = "lili" //静态变量

        fun showInfo() {
            println(info)
        }
    }

    fun test() {
        //背后代码:生成了 Companionobject.companion类
        println(info)

        showInfo()
    }
}
Kotlin 接口
//kotlin的接口
//接口里面的所有成员和方法和接口本身都是 public open的
//实现类不仅要重写接口的函数,还要重写接口的成员
interface IUSB {
    var usbVersionInfo: String

    fun insertUSB(): String
}

//成员重写写到实现类的构造方法中 方式
class Mouse(override var usbVersionInfo: String = "USB 3.0") : IUSB {
    override fun insertUSB(): String = usbVersionInfo
}

//成员重写写到实现类中的方式
class KeyBorad: IUSB {
    override var usbVersionInfo: String = "USB 3.0"
        get() {  //get/set 方法会调用代码块内容,并返回一个值
            println("获取了 usbVersionInfo ${usbVersionInfo}")
            return field
        }
        set(value) {
            println("设置了 usbVersionInfo ${usbVersionInfo}")
            field = value //field表示该 usbVersionInfo 属性
        }

    override fun insertUSB(): String = "keyboard $usbVersionInfo"

}

fun showInterface() {
    val usb1 = Mouse()
    usb1.insertUSB()

    val usb2 = KeyBorad()
    usb2.usbVersionInfo = "sfd" //使用属性调用get()  赋值属性调用set()
    usb2.insertUSB()
}
JvmField JvmStatic
// JvmStatic JvmField注解使用
//用于 Java调用kotlin 属性和方法 兼容性 而产生的
//JvmField注解使用 将默认的private修改成了public访问符
//JvmStatic 将 companion object 中的 属性、方法能像kotlin一样能直接调用
class Personz {
    @JvmField
    val names = listOf("lala", "haha", "yaya")

    //这个 names转成java就是如下代码, val修饰的 names属性成了 private final ,外部调用不到,而提供了一个方法获取
    // 使用了 @JvmField注解,将该属性从private变成public,能直接获取val修饰的属性
    /**
     * public final class Personz {
     *    @NotNull
     *    private final List names = CollectionsKt.listOf(new String[]{"lala", "haha", "yaya"});
     *
     *    @NotNull
     *    public final List getNames() {
     *       return this.names;
     *    }
     * }
     */

    companion object {
        @JvmField
        val name : String = "lala"

        @JvmStatic
        fun printlnPersonName() = println("名字: " + name)
    }
}

//java中调用代码
    public void getJvmFieldStatic() {
//        new Personz().getNames();  //没加 jvmField的调用
        List<String> names = new Personz().names; //加了 jvmField的调用

        //没加 JvmStatic,java调用 Companion object需要加一层 Companion类来调用
//        Personz.Companion.getName();
//        Personz.Companion.printlnPersonName();

        String name = Personz.name;
        //加上 JvmStatic,就有了和Kotlin一样的调用效果,把函数写到了 Companion类 外面
        Personz.printlnPersonName();
    }
range符
    fun range(number: Int) {
        //range 范围
        if (number in 10..59) {
            println("不及格")
        } else if (number in 0 .. 9) {
            println("很差")
        } else if (number in 60..100) {
            println("及格")
        } else {
            println("分数不合法")
        }
    }
将现有Java文件转换为Kotlin文件

右键->Convert Java File to Kotlin File
我们可以将现有Java文件代码转换为Kotlin文件代码,而实现从java往kotlin上转移。可以针对单个文件,某个包,某个module,或者整个项目转为kotlin文件。
转换完成之后可能会有少量语法错误,需要手动修改一下。

将kotlin代码转为java

在Tools->Kotlin->show Kotlin ByteCode,点击Decompile转回类似Java代码。

教程参考:

https://www.bilibili.com/video/BV1kT4y1o7nP

Github代码地址:

https://github.com/running-libo/KotlinPractise

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270