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

以下是我的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不是一个扩展函数,它是将参数执行了方法

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