美文网首页
Kotlin 属性与函数入门(与Java 比对)

Kotlin 属性与函数入门(与Java 比对)

作者: _Jun | 来源:发表于2022-05-19 15:50 被阅读0次

    前言

    Kotlin 问世也有好几年了,尤其作为Android 官方指定的开发语言,许多项目已经替换为Kotlin编写,广泛流行的第三方库也开始支持Kotlin。在使用Kotlin的过程中,你将会逐渐了解其优势并学会物尽其用。
    通过本篇文章,你将了解到:

    1、为什么需要属性和函数
    2、Kotlin 属性详解
    3、Kotlin 函数详解

    1、为什么需要属性和函数

    简单来说:

    属性就是入参,函数就是处理变量的过程体

    因此,在高级语言范畴,属性和函数是基础。

    2、Kotlin 属性详解

    属性声明

    Kotlin 属性声明的关键词是var 与 val,var 是 variable的缩写,val 是 value的缩写。var 指向的值是可变的,后续可以给它赋其它值,而val 是常量,初始化后就不能再变化。
    属性分为两种:

    常量&变量

    //kotlin
    //变量
    var num: Int = 3
    //常量
    val age: Int = 4
    
    //java
    //变量
    int num = 3;
    //常量
    final int age = 4;
    
    

    与Java 属性声明不同的是,Kotlin 需要使用var/val 声明属性,并且属性类型写在":"后边,你可能会说了:说好的Kotlin简洁呢,咋还多写了几个单词?
    实际上Kotlin 可以进行类型推断,比如上面声明的属性可以改写为如下:

    //kotlin
    //变量
    var num = 3
    //常量
    val age = 4
    
    

    省略了":"与类型,编译器自动推断num与age 的类型为Int。
    当然,若是:

    //kotlin
    //变量
    var num = 3.4
    
    

    那么此时num 为Double 类型。

    属性类型

    可以看出,Kotlin里不存在所谓的基本数据类型,而它里边的"基本数据类型引用"与Java 基本数据类型对应,类似Java 的包装类。
    来看看数据类型转换:

    var numInt : Int = 4
    var numLong : Long = 5
    
    //kotlin 转换
    fun test() {
        //不被允许
        numInt = numLong
        numLong = numInt
    
        //允许
        //大范围转小范围可能会发生溢出
        numInt = numLong.toInt()
        numLong = numInt.toLong()
    }
    
    //Java 转换
    void test() {
        int a = 5;
        //小转大  允许
        long b = a;
        //大转小 允许
        a = (int) b;
    }
    
    

    可见:Kotlin 大转小、小转大需要依赖toXX()函数才行。

    属性访问

    全局属性

    以上声明属性/函数定义在:VarTest.kt里,现在分别从另一个Kotlin文件和Java 文件里访问它们。

    Kotlin 里访问

    #Start.kt
    fun main(args:Array<String>) {
        //赋值
        num = 3.4
        //取值
        var numLong2 = num
        //调用函数
        test()
    }
    
    

    Java 里访问

        void test2() {
            //调用方法
            VarTestKt.test2();
            //获取变量
            VarTestKt.getNum();
            //设置变量
            VarTestKt.setNum(33);
        }
    
    

    在VarTest.kt里并没有声明类,因此直接在文件里声明的属性/函数 是具备全局特性的,最终编译后的字节码里:

    属性是静态属性,函数是静态方法。VarTest.kt会编译为VarTestKt Java类,因此在Java 代码里可以通过静态方法访问它们。
    而在Kotlin 里直接访问它们。

    VarTest.kt编译后字节码反编译如下:

    #TestJava.java
    public final class VarTestKt {
        private static double num = 3.4D;
        
        public static final double getNum() {
            return num;
        }
    
        public static final void setNum(double var0) {
            num = var0;
        }
        
        public static final void test2() {
            ...
        }
    }
    
    

    如果想在Java 文件里直接通过.xx的方式访问属性,需要在VarTest.kt 文件里添加@JvmField 修饰

    #VarTest.kt
    @JvmField
    var num = 3.4
    
    

    在Java 里调用如下:

    #TestJava.java
        void test2() {
            //调用方法
            VarTestKt.test2();
    //        //获取变量
    //        VarTestKt.getNum();
    //        //设置变量
    //        VarTestKt.setNum(33);
    
            //直接访问属性
            VarTestKt.num = 2.4;
            double num = VarTestKt.num;
        }
    
    

    需要注意的是:对于Java 来说,此时num 属性的访问权限为public,并且其默认的get/set 方法都没有了,在Java里只能通过.num访问它,而不通过getNum/setNum访问。

    类成员属性

    先声明属性
    #VarTestClass.kt
    class VarTestClass {
        var num:Int = 3;
    }
    
    

    Kotlin 里访问

    fun main1() {
        //新建对象
        var varTestClass = VarTestClass()
        //赋值
        varTestClass.num = 3
        //获取值
        var num = varTestClass.num
    }
    
    

    Java 里访问

    #TestJava.java
        void test3() {
            //新建对象
            VarTestClass varTestClass = new VarTestClass();
            varTestClass.getNum();
            varTestClass.setNum(3);
        }
    
    

    与全局属性相比,类成员属性依赖于类,因此想要访问它们需要先新建对象,通过对象来访问。其余访问方式两者一致。

    属性权限与初始化

    访问控制权限

    Koltin 默认是public,因此大多数情况下都可以直接访问属性。
    若是使用private 修饰属性,那么将不会生成get/set 方法。

    初始化

    初始化时机
    在Java里 声明属性的同时给属性赋值即可完成属性初始化,而对于Kotlin来说可以选择不立刻初始化。 先看常量val 初始化:

    //正常初始化
    val myNum = 4
    //延迟初始化 常量
    val myNum2:Int by lazy { 3 }
    
    

    看起来延迟初始化没啥用处呢?再来看引用类型延迟初始化

    class MyBean {
        var age = 3
    }
    //正常初始化
    val myBean = MyBean()
    //延迟初始化
    val myBean1:MyBean by lazy { 
        MyBean()
    }
    
    

    这么看作用就比较明显了,当用到myBean1时才会构造MyBean对象,节省了内存。
    注:by 和 lazy 都是函数
    当我们使用by lazy 延迟初始化变量时会报错。

    var myBean1:MyBean by lazy {
        MyBean()
    }
    
    

    变量的延迟初始化需要另一个关键字:lateinit

    //变量正常初始化
    var name:String = "fish"
    //变量延迟初始化
    lateinit var name1:String
    
    fun useName() {
        name1 = "fish1"
    }
    
    

    需要注意的是,以下方式将不被允许:

    //错误,不能修饰基本类型
    lateinit var num3:Int
    //错误,不能修饰空类型
    lateinit var name2?:String
    
    

    总结来说,属性可以选择延迟初始化:

    var 使用 lateinit,该关键字不能修饰基本类型(Int等)
    val 使用 by lazy

    get/set 方式
    在Java 里对于属性我们一般使用get/set 对它进行操作,Kotlin 里虽然自动生成了get/set 方法,但有时我们需要对get/set 进行额外操作,因此可以重写它们。

    var myName: String = ""
        get() {
            //获取值
            var age = 2
            if (age > 18)
                return field
            return ""
        }
        set(value) {
            //设置值
            if (value == "fish")
                field = value
        }
    
    

    field 称为后端变量,是变量的实际值,此处为myName的实际值。

    注意:get/set 里不能使用myName,因为访问myName本质上是通过get/set 方法进行的,会造成无限递归访问,因此需要使用field。

    3、Kotlin 函数详解

    名称不同

    在Java 里:

        void test4() {
            
        }
    
    

    称为方法
    而在Kotlin里:

    fun test4() {
        
    }
    
    

    称为函数

    形参与返回值

    fun test4(name : String):String {
        return "hello"
    }
    
    

    声明函数需要自定关键字:fun
    与属性的定义类似,: 后表示返回类型。
    name:String 表示形参。

    看起来和Java 相差不大,接着来看细节之处。
    Kotlin 函数若是没有返回值,则用Unit表示:

    fun test5(name: String):Unit {
    }
    
    

    类似Java里的Void。
    通常来说,此处的Unit可以省略,变为如下:

    fun test5(name: String) {
    }
    
    

    命名参数与默认参数

    先说命名参数:

    //函数声明
    fun testV2(name: String, age: Int, score: Double) {
    }
    
    

    调用该函数:

    fun main2() {
        testV2("fish", 5, 4.5)
    }
    
    

    当我们看testV2调用时,可能无法一下子看出实参和形参如何对应上的,此时就需要命名参数(具名参数):

    fun main2() {
        testV2("fish", 5, 4.5)
        testV2(score = 4.5, name = "fish", age = 5)
    }
    
    

    "命名"顾名思义可以指定形参的名字与实参的值,当使用命名参数时,参数的顺序可以调换。
    需要注意的是:命名参数的后面跟的其它参数也需要写成命名参数的形式。
    比如以下就是编译错误:

        testV2(score = 4.5, name = "fish", 5)
    
    

    再说默认参数:

    fun testV4(name: String = "fish", age: Int, score: Double){
    }
    
    

    name 默认值是"fish",调用testV4()如下:

    fun  main3() {
        testV4(score = 4.5, age = 5)
    }
    
    

    此时若是name值没变化,就可以不用传递,使用默认值即可。
    需要注意的是:当不传递默认参数时,其它参数需要使用命名参数的方式传递。
    当然,如果默认参数是最后一个值,那么其它参数可以直接传递值而无需使用命名参数形式。

    fun testV4(name: String = "fish", age: Int, score: Double = 4.5){
    }
    fun  main3() {
        testV4("fish", 4)
    }
    
    

    Java 中没有命名参数和默认参数的说法。

    可变参数

    Java 中的可变参数:

        void test(String... names) {
            for (String name : names) {
                Log.d("fish", name);
            }
        }
    
    

    Koltin中可变参数为:

    fun testV5(vararg name:String) {
        name.forEach {
            println(it.length)
        }
    }
    
    

    通过vararg 声明。
    在Kotlin 里调用如下:

    fun  main4() {
        testV5("fish", "fish2", "fish3")
    }
    
    

    函数分类

    依据函数作用域的不同,可分为以下几种:

    顶层函数

    定义在Koltin 文件里的函数,与类无关。
    在Start.kt 文件里定义如下:

    fun  main4() {
        testV5("fish", "fish2", "fish3")
    }
    
    class MyStart {
        
    }
    
    

    此时main4 与MyStart 没有任何关系,它是存在于Start.kt里的,其它文件访问该方法与访问全局(顶层)属性类似。

    类成员函数

    class MyStart {
        fun start() {
           
        }
    }
    
    

    start() 为成员函数,外部访问它与访问类成员属性类似。

    类构造函数

    涉及知识点较多,下篇分析。

    嵌套函数

    顾名思义:放在函数里的函数。

    class MyStart {
        fun start() {
            fun end() {
            }
            //调用
            end()
        }
    }
    
    

    此时end 为嵌套函数,主要用途:

    1、递归函数。
    2、函数内部统一逻辑抽取,又不想暴露给外部。

    与Java 比对,Kotlin 属性变动也不是特别大,真正变化大的是Kotlin 的函数。
    以上只是简单介绍了函数的一些基本知识,它还有一些更高级的功能:
    扩展函数、函数参数/返回值 为函数、函数表达式、函数泛型、Lambda等,理解了这些知识是看懂协程的前提。
    我们下篇将补齐这些知识点。

    本文基于Kotlin 1.5.3,文中Demo请点击

    相关文章

      网友评论

          本文标题:Kotlin 属性与函数入门(与Java 比对)

          本文链接:https://www.haomeiwen.com/subject/odptprtx.html