美文网首页
kotlin - 函数(对象函数/代码补全)

kotlin - 函数(对象函数/代码补全)

作者: 前行的乌龟 | 来源:发表于2019-02-22 18:25 被阅读15次

    kotlin 虽然好上手,但是 kotlin 自身也是特性的,纯按照 kotlin 写的代码会让初上手的同学看不懂的。对于我来说一开始 kotlin 比较难习惯的就是对象函数了

    kotlin 可是真正的一切皆对象,kotlin 允许你把 method 作为参数使用,既可以作为全局参数使用,也可以在方法中作为参数传入,这点我觉得是 kotlin 这类语言的最大特点,通过对象函数,我们可以不用再像 java 时代写一大堆 interface 啦

    但是使用 kotlin 后,怎么让代码如同 java 一样实现代码补全让我着实费了一番劲,本文重点就在于如何实现 kotlin 对象函数的代码补全

    java 时代


    java 时代我们这样使用接口参数

        // 声明一个接口类型
        interface CustomeClick {
    
            void click();
        }
    
        // 创建一个传入 CustomeClick 类型参数的方法
        public void click(CustomeClick click) {
    
        }
    
        // 具体使用我们声明的方法
        public void main() {
    
            click(new CustomeClick() {
                @Override
                public void click() {
    
                }
            });
        }
    

    在 java 时代我们要频繁的去创建 interface 接口类型,尤其是对于只有一个方法的接口我们使用的最多,声明接口时我们还要取思考接口名叫什么合适,接口放在哪里,费时费力,最后也只是干了一个传参的事

    我们按 Command + P 可以看到参数提示:


    然后我们 new + 接口名可以自动补全代码:


    随后 java 提供了 lambda 表达式来简化代码,但是当时我看就有人说 java 搞了个半拉子,不看好 lambda ,当时我是不懂,用过 kotlin 以后才知道为啥

    max(strings,    {   a,  b   ->  a.length    <   b.length    })
    

    我的习惯就是上面这样的,先看看参数类型,然后 new 一下代码就出来了,然后我们写方法实现就好了,用什么久了就成习惯了,这点让我在最初切换到 kotlin 时极不适应,kotlin 在这点上和 java 有巨大的操作差异,需要适应

    kotlin 时代


    kotlin 的上位,给我们提供了更便利的写法, kotlin 支持对象函数,可以把方法作为一个参数去使用了,再具体代码上我们可以不用再写 iterface 接口了

    首先在方法中声明对象函数限定描述,主要是传什么类型参数,返回什么类型参数

        // left: 是函数对象的名字,() 里面限定传入参数 ,-> 限定返回值类型,没有返回值要显示的写 Unit 
        fun cks(price: Int, left: (name1: String) -> String, right: (name2: String) -> String) { ... }
    
        // 函数对象若是只有一个,并且在最后的位置,可以简写
        fun <T> lock(lock: Lock, body: () -> T): T { ... }
    

    kotlin Lambda表达式,函数作为成员变量声明

    // 不带返回值写法
    val sum = { x: Int, y: Int -> x + y }
    // 带返回值写法
    val sum: (Int, Int) -> Int = { x, y -> x + y }
    
    val i: Int = sum(1, 2)
    

    我们来看看官方的例子:

    // 这是集合对象的过滤函数,可以看到官方的例子是比较复杂的,还涉及到其他一些知识点
    public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
        return filterTo(ArrayList<T>(), predicate)
    }
    

    然后我们来看看 kotlin 的自动提示,自动补全好不好使

    同样是 Command + P 我们依然可以参数提示:


    但是 kotlin 没有 new 了,我们不能快速的实现代码补全了,只能自己写了,说实话这点我不喜欢

    标准写法: { 再次声明参数(在这可以改名,有的系统定义的参数名我甚是不喜欢) + -> + 方法体实现 }

            cks(3,
                    { nameLeft: String ->
                        nameLeft + "11"
                        nameLeft + "22"
                    },
                    { nameRight: String ->
                        nameRight + "11"
                        nameRight + "22"
                    }
            )
    

    简写:可以不用再声明参数,直接写方法体实现,参数名由 it 代替,it 这块系统有默认提示的

            cks(3,
                    {
                        it + "11"
                        it + "22"
                    },
                    { 
                        it + "11"
                        it + "22"
                    }
            )
    

    函数对象要是有2个参数,那么我们必须重写参数声明,it 只能节省单个参数时的代码,这时我们可以 Command + P 看参数提示,然后照着第一个参数把名字敲出来,系统的自动提示框里有快速补全参数声明的选项


    上面是使用函数对象时代码自动补全怎么用的说明,kotlin 当然还支持传统的 iterface 接口参数,这时代码自动补全怎么用

    我们使用 object:(接口名){ } 来声明一个匿名实现类,然后在 {} 内使用 Command + Ins 也可以实现代码补全


            name( object :Foo{
                override fun acg() {
                    ...
                }
            })
    

    vararg 可变参数

    //用 vararg 修饰符标记参数
    fun <T> asList(vararg ts: T): List<T> {
        val result = ArrayList<T>()
        for (t in ts) // ts is an Array
            result.add(t)
        return result
    }
    
    val a = arrayOf(1, 2, 3)
    //*a代表把a里所有元素
    val list = asList(-1, 0, *a, 4)
    //运行代码,得到结果为: [-1, 0, 1, 2, 3, 4]
    

    内嵌函数

    kotlin 允许你在函数内部声明作用域仅仅在函数内部的函数

    fun foo() {
        println("outside")
        fun inside() {
            println("inside")
       }
       inside()
    }
    
    //调用foo()函数
    foo()
    
    outside
    inside
    

    泛型

    函数中的泛型有必要展示一下,谁知道哪天突然就记不清了呢~

    Java代码

    <T> void print(T t) { ... }
    
    <T> List<T> printList(T t) { ... }
    

    Kotlin代码

    fun <T> printList(item: T) { ... }
    
    fun <T> printList(item: T): List<T> { ... }
    

    tailrec 递归

    不写 tailrec 就是死循环,即使他是递归,有退出机会

    tailrec fun count(x: Int = 1): Int = if (x == 10) x else count(x - 1)
    

    inline / noinline 内联函数

    上面我们说了 kotlin 允许我们把方法作为对象来使用,这样虽说方便了,但是 kotlin 也会生成相应的函数对象,在内存开销上一定性能损失,基本虽有讲这块的文章都有一句话

    使用高阶函数会带来一些运行时的效率损失。每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。这时可以通过内联函数消除这类的开销。

    使用 inline 的话就会有特殊的操作,把函数内联到调用处使用,用法很简单,就是在目标 fun 前面加上 inline 限定

        inline fun cks(left: (name1: String) -> String, right: (name2: String) -> String) { ... }
    

    inline 的注意点是,避免内联比较大的函数,因为内联可能导致生成的代码增加。如果想禁用一些函数的内联,可以使用 noinline 修饰符要禁用的函数

        inline fun cks(left: (name1: String) -> String, noinline right: (name2: String) -> String) { ... }
    

    扩展函数

    kotlin 允许我们在类中对已经定义好的类进行扩展,包括方法和属性 ,但是作用域只在当前类内

    • 扩展函数 / 扩展属性
        // 扩展函数
        fun String.any(name: String) {
            Log.d("AAA", "扩展函数 - String.any 运行!!!")
        }
    
        // 扩展属性,注意必须要写 get/set ,然后扩展属性只能声明在成员变量
        var News.name: String
            get() = name
            set(value) {
                name = value
            }
    
    • 扩展伴生对象
    // 扩展伴生对象
    class User {
        companion object {
        }
    }
    
    fun User.Companion.foo() {
        println("伴生对象扩展")
    }
    
    User.foo()
    
    • 扩展函数经典例题
    open class D {
    }
    
    class D1 : D() {
    }
    
    open class C {
        open fun D.foo() {
            println("D.foo in C")
        }
    
        open fun D1.foo() {
            println("D1.foo in C")
        }
    
        fun caller(d: D) {
            d.foo()   // 调用扩展函数
        }
    
        fun caller2(d1: D1) {
            d1.foo()   // 调用扩展函数
        }
    }
    
    class C1 : C() {
        override fun D.foo() {
            println("D.foo in C1")
        }
    
        override fun D1.foo() {
            println("D1.foo in C1")
        }
    }
    
    //调用
    C().caller(D())   
    C1().caller(D()) 
    C().caller(D1()) 
    C().caller2(D1())
    C1().caller2(D1())
    
    D.foo in C
    D.foo in C1
    D.foo in C
    D1.foo in C
    D.1foo in C1
    

    这个例子非常好,能让我们高明白扩展函数的作用域,尤其是第三个 虽然传入的参数是 C1,但是我们接收的参数类型是 C,所以扩展函数的作用域就在 C 里面,这里子类的多态不起作用,要注意


    接口委托

    接口委托其实就让我们在类中可以不用实现 A 接口的方法, 而是转接到传入的实现类参数上

    //定义一个接口 Base
    interface Base {
        fun print()
    }
    
    //定义一个 ImplBase 实现接口 Base 
    class ImplBase(val i: Int) : Base {
        override fun print() {
            println(i)
        }
    }
    
    //定义一个 Drived 类实现接口 Base 
    class Drived(b: Base) : Base {
        //这里需要 override 接口 Base 里的方法
        override fun print() {
        }
    }
    
    //如果使用委托模式的话,可以把 Base 里的方法委托给 Drived
    class Drived(b: Base) : Base by b
    
    //调用 print() 方法
    var b = ImplBase(10)
    Drived(b).print()
    
    //运行代码,打印结果为 10
    

    相关文章

      网友评论

          本文标题:kotlin - 函数(对象函数/代码补全)

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