美文网首页kotlin
kotlin进阶—函数、泛型、高级特性

kotlin进阶—函数、泛型、高级特性

作者: Peakmain | 来源:发表于2020-06-30 17:19 被阅读0次

    以下是我的kotlin系列文章
    kotlin基础知识—基本语法(一)
    kotlin基础知识—基本语法(二)

    前言

    高阶函数

    • 接受函数作为参数或者返回一个函数的函数就叫做高阶函数
    fun cost(block: () -> Int) {
        val start = System.currentTimeMillis()
        block()
        println(System.currentTimeMillis() - start)
    }
    
    fun sum(a: Int, b: Int): Int {
        return a + b
    }
    fun main() {
        cost{
            sum(4,5)
        }
    }
    

    内联函数

    内联函数使用关键字inline内联声明,内联函数的使用增强了高阶函数的性能。 内联函数告诉编译器将参数和函数复制到调用站点。
    看个例子,我们将刚刚上面高阶函数的代码转成对应Java代码

    public final class KotlinKt {
       public static final void cost(@NotNull Function0 block) {
          Intrinsics.checkParameterIsNotNull(block, "block");
          long start = System.currentTimeMillis();
          block.invoke();
          long var3 = System.currentTimeMillis() - start;
          boolean var5 = false;
          System.out.println(var3);
       }
    
       public static final int sum(int a, int b) {
          return a + b;
       }
    
       public static final void main() {
          cost((Function0)null.INSTANCE);
       }
    
       // $FF: synthetic method
       public static void main(String[] var0) {
          main();
       }
    }
    

    我们对cost函数加上inline

    inline fun cost(block: () -> Int) {
        val start = System.currentTimeMillis()
        block()
        println(System.currentTimeMillis() - start)
    }
    
    fun sum(a: Int, b: Int): Int {
        return a + b
    }
    fun main() {
        cost{
            sum(4,5)
        }
    }
    

    转成对应的Java代码

    public final class KotlinKt {
       public static final void cost(@NotNull Function0 block) {
          int $i$f$cost = 0;
          Intrinsics.checkParameterIsNotNull(block, "block");
          long start = System.currentTimeMillis();
          block.invoke();
          long var4 = System.currentTimeMillis() - start;
          boolean var6 = false;
          System.out.println(var4);
       }
    
       public static final int sum(int a, int b) {
          return a + b;
       }
    
       public static final void main() {
          int $i$f$cost = false;
          long start$iv = System.currentTimeMillis();
          int var3 = false;
          sum(4, 5);
          long var4 = System.currentTimeMillis() - start$iv;
          boolean var6 = false;
          System.out.println(var4);
       }
    
       // $FF: synthetic method
       public static void main(String[] var0) {
          main();
       }
    }
    

    我们可以看到inline将函数本身内联到调用处,并且函数的参数也被内联到调用处

    高阶函数的return

    val datas= intArrayOf(1,2,3,4)
    fun main(){
        datas.forEach {
            if(it==3)return
            println("$it")
        }
    }
    

    转成对应的Java代码

       public static final void main() {
          int[] $this$forEach$iv = datas;
          int $i$f$forEach = false;
          int[] var2 = $this$forEach$iv;
          int var3 = $this$forEach$iv.length;
    
          for(int var4 = 0; var4 < var3; ++var4) {
             int element$iv = var2[var4];
             int var7 = false;
             if (element$iv == 3) {
                return;
             }
    
             String var8 = String.valueOf(element$iv);
             boolean var9 = false;
             System.out.println(var8);
          }
       }
    

    return是直接退出整个for循环

    return @forEach的使用

    val datas= intArrayOf(1,2,3,4)
    fun main(){
        datas.forEach {
            if(it==3)return@forEach
            println("$it")
        }
    }
    

    转成对应Java代码

       public static final void main() {
          int[] $this$forEach$iv = datas;
          int $i$f$forEach = false;
          int[] var2 = $this$forEach$iv;
          int var3 = $this$forEach$iv.length;
    
          for(int var4 = 0; var4 < var3; ++var4) {
             int element$iv = var2[var4];
             int var7 = false;
             if (element$iv != 3) {
                String var8 = String.valueOf(element$iv);
                boolean var9 = false;
                System.out.println(var8);
             }
          }
       }
    

    实际上return@forEach是跳过指定条件

    object类的使用

    用object替换class类

    object A{
        val age:Int=0
    }
    

    转成对应Java代码

    public final class A {
       private static final int age = 0;
       public static final A INSTANCE;
    
       public final int getAge() {
          return age;
       }
    
       private A() {
       }
    
       static {
          A var0 = new A();
          INSTANCE = var0;
       }
    }
    

    我们可以看到object替换类的使用实际就是一个单例,并将所有属性设置为static

    如果想做静态双重校验所单例,怎么做?

    class A{
        var instance: A? = null
            get() {
                if (field == null) {
                    synchronized(this) {
                        field = A()
                    }
                }
                return field
            }
    
        companion object {
            private var instance: A? = null
        }
    }
    
    • 推荐单例写法
    class Single private constructor(){
        companion object{
            fun get():Single{
                return Holder.instance
            }
        }
        private object Holder{
            val instance=Single()
        }
    }
    

    by没有反射的动态代理

    interface View {
        fun click()
    }
    class TextView :View{
        override fun click() {
            println("View点击")
        }
    }
    class ViewGroup(view:View):View by view
    fun main() {
        ViewGroup(TextView()).click()
    }
    

    结果:打印View点击。
    转成对应的Java代码,主要看ViewGroup

    public final class ViewGroup implements View {
       // $FF: synthetic field
       private final View $$delegate_0;
    
       public ViewGroup(@NotNull View view) {
          Intrinsics.checkParameterIsNotNull(view, "view");
          super();
          this.$$delegate_0 = view;
       }
    
       public void click() {
          this.$$delegate_0.click();
       }
    }
    

    此时,我们发现kolin在转成Java之后,会将动态代理转成静态代理

    泛型

    Java中的泛型
    我们都知道下面这行代码会报错

      List< TextView> mViews=new LinkedList<Button>();
     //或者
     List<Button> mButtons = new LinkedList<TextView>();
    
    • 理解通配符? extends和?supre
      在继承关系中,子类是继承于父类的,所以我们可以理解为父类在上,子类在下,所以子类想赋值给父类,需要使用? extends上界,且我们知道TextView是button的父类,所以改成如下
    List<? extends TextView> mViews = new LinkedList<Button>();
    

    同理,父类向赋值给子类,就需要下界,也就是super

     List<? super Button> mButtons = new LinkedList<TextView>();
    

    ? extends只能读取不能修改(修改指的是添加元素)
    ? super只能修改不能读取

    kotlin中的out和in
    使用

    var mViews: MutableList<out TextView?>? = LinkedList<Button>()
    var mButtons: MutableList<in Button> = LinkedList<TextView>()
    

    out表示只能输出不能输入,你只能读我不能写我(相当于? extends)。in则相反,只能输入不能输出,你只能写我不能读我(相当于? super)

    *

    • Java中的?也可以作为泛型通配符,相当于? extends Object
    • 而kotlin等效的用法是 *,相当于? extends Any
    • 和 Java 不同的地方是,如果你的类型定义里已经有了 out 或者 in,那这个限制在变量声明时也依然在,不会被 * 号去掉。

    reified关键字
    如果想知道泛型T是什么类型,Java中是无法知道的,因为Java具有泛型擦除功能

    image.png

    而kotlin中可以使用reified关键字,reified必须和inline一起使用

    class A {
        inline fun <reified T> test(item: Any?) {
            if (item is T) { 
                println(item)
            }
        }
    }
    

    高级特性

    解构

    fun main() {
        val user = User(18, "peakmain")
        val (age, name) = user //👈解构
        println(age)
        println(name)
    }
    
    class User(val age: Int, var name: String) {
        operator fun component1()=age//👈必须是component+数字
        operator fun component2()=name
    }
    

    解构可以自动把一个对象拆成一个个属性。解构用的更多的是在map集合中

    fun main() {
        val map = mapOf("peakmain" to 18, "Treasure" to 19)
        for ((key,value) in map){
            println("$key——$value")
        }
    }
    

    kotlin海量操作符

    • rxjava操作符
      说到操作符,我们通常会想到rxjava,rxjava也具有丰富的操作符
       public static void main(String[] args) {
            final String[] a = new String[]{"4", "0", "7", "i", "f", "w", "0", "9"};
            final Integer[] index = new Integer[]{5, 3, 9, 4, 8, 3, 1, 9, 2, 1, 7};
            Observable.just(index)
                     //拆成一个个integer
                    .flatMap(new Function<Integer[], ObservableSource<Integer>>() {
                        @Override
                        public ObservableSource<Integer> apply(Integer[] integers) throws Exception {
                            return Observable.fromArray(integers);
                        }
                    })
                    .filter(new Predicate<Integer>() {
                        @Override
                        public boolean test(Integer integer) throws Exception {
                            return integer < a.length;
                        }
                    })
                    .map(new Function<Integer, String>() {
                        @Override
                        public String apply(Integer integer) throws Exception {
                            return a[integer];
                        }
                    }).reduce(new BiFunction<String, String, String>() {
                @Override
                public String apply(String s, String s2) throws Exception {
                    return s + s2;
                }
            }).subscribe(new Consumer<String>() {
                @Override
                public void accept(String s) throws Exception {
                    System.out.println(s);
                }
            });
        }
    

    那么同样的功能kotlin如何实现的呢

    fun main() {
        val a = arrayOf("4", "0", "7", "i", "f", "w", "0", "9")
        val index = arrayOf(5, 3, 9, 4, 8, 3, 1, 9, 2, 1, 7)
        index
                .filter {
                    it < a.size
                }.map {
                    a[it]
                }.reduce { s, s1 ->
                    s + s1
                }
                .also {
                    println(it)
                }
    }
    

    filter和rxjava的filter一样,代表过滤,map转换,reduce从第一项到最后一项进行累计,会将上个结果继续操作,最后一次输出,also相当于rxjava的subscribe

    几个常用的作用域函数

    • run函数
     val user: User = User("peakmain")
        user.run { "let::${this.name}" }
    

    源码分析

    public inline fun <T, R> T.run(block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block()
    }
    

    我们可以看到这实际就是将T进行扩展,扩展的方法为run,且没有闭包参数

    • let
      let和run都会返回闭包执行的结果,let有闭包参数,run没有闭包参数
      使用
       val user: User = User("peakmain")
        user.let { "let::${user.name}" }
    

    直接看源码

    public inline fun <T, R> T.let(block: (T) -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block(this)
    }
    
    

    我们可以看到run和let的主要区别在于,run是this.T,而let是参数T,所以推荐使用let,且有闭包参数

    • also和apply
      laso和apply都不会返回闭包执行结果,区别在于also有闭包参数,apply没有闭包参数
      also源码
    public inline fun <T> T.also(block: (T) -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block(this)
        return this
    }
    

    apply源码

    public inline fun <T> T.apply(block: T.() -> Unit): T {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        block()
        return this
    }
    

    同理:推荐also

    • takeIf和takeUnless
      takeIf的闭包返回一个判断结果,为false时,takeIf函数会返回空
      takeUnless则相反,闭包的结果为true的时候返回空
    • repeat
      重复执行当前闭包
    • use
      当我们需要关闭一些流,可使用此方法有效避免内存泄漏
      源码
    public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
        var exception: Throwable? = null
        try {
            return block(this)
        } catch (e: Throwable) {
            exception = e
            throw e
        } finally {
            when {
                apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
                this == null -> {}
                exception == null -> close()
                else ->
                    try {
                        close()
                    } catch (closeException: Throwable) {
                        // cause.addSuppressed(closeException) // ignored here
                    }
            }
        }
    }
    
    • with
    var datas= intArrayOf(1,2,3,4)
    fun test(){
     with(datas,{
         datas[0]=11
         datas[1]=12
     })
    }
    

    源码

    public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return receiver.block()
    }
    

    所以上面代码实际类似下面代码

        datas.also {
            datas[0] = 11
            datas[1] = 12
        }
    

    with不是一个扩展函数,它是将参数执行了方法

    相关文章

      网友评论

        本文标题:kotlin进阶—函数、泛型、高级特性

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