Kotlin-扩展

89 篇文章 0 订阅
10 篇文章 0 订阅

一、扩展函数

1、扩展函数

扩展函数,就是从类的外部扩展出来的一个函数,这个函数看起来就像是类的成员方法一样。比如扩展一个String的方法。我们经常需要切割字符串然后获取第几个元素,我们扩展一个类似的工具类方法,如下:

fun String.getStringAfterCut(regular: String, index: Int): String? {
    split(regular).let { list ->
        return list.getOrNull(index)
    }
}
复制代码

调用

"a.5.c".getStringAfterCut(".",1)

扩展函数中扩展的对象String在函数中被成为函数的接收者

2、扩展函数的原理

我们将扩展函数和调用的方法都反编译,会得到下面的结果:

//扩展函数反编译
//省略了几行不影响分析的代码
public final class StringKtKt {
   @Nullable
   public static final String getStringAfterCut(@NotNull String $this$getStringAfterCut, @NotNull String regular, int index) {
    //...
      List var3 = StringsKt.split$default((CharSequence)$this$getStringAfterCut, new String[]{regular}, false, 0, 6, (Object)null);
    //...
      return (String)CollectionsKt.getOrNull(var3, index);
   }
}
//调用处的反编译
StringKtKt.getStringAfterCut("a.5.c", ".", 1);

可以看到扩展函数并没有成为String类的成员方法,仅仅是生成了StringKtKt类的静态方法,而扩展的对象被当做参数传入进了这个静态方法中。

二、扩展属性

1、扩展属性

扩展属性,是在类的外部为它定义一个新的成员属性。例如下面的方法,获取字符串的第一个字符:

val String.firstChar: Char?
    get() = if (isEmpty()) {
        null
    } else {
        get(0)
    }

调用

"a.5.c".firstChar

2、扩展属性的原理

反编译上面的代码看看结果:

//扩展属性反编译代码
//省略了几行不影响分析的代码
public final class StringKtKt {
   @Nullable
   public static final Character getFirstChar(@NotNull String $this$firstChar) {
      //...
      return var1.length() == 0 ? null : $this$firstChar.charAt(0);
   }
}
//调用处的反编译
StringKtKt.getFirstChar("a.5.c");

可以看到和扩展函数类似,扩展属性并没有成为String类的成员属性,仅仅是生成了StringKtKt类的静态方法,而扩展的对象被当做参数传入进了这个静态方法中。

三、扩展函数和扩展属性应该如何选择

有些扩展可以写成扩展函数也可以写成扩展属性,那么应该如何选择? 如果语义上适合作为属性那么写成扩展属性比较合适,比如firstChar,语义上适合作为扩展函数则写成扩展函数。

四、扩展的访问域

扩展的访问域只讨论扩展函数,扩展属性类似。

顶层扩展:扩展的访问域仅限于该Kotlin文件当中的所有成员,以及被扩展类型的公开成员,这种方式定义的扩展是可以被全局使用的。

类内扩展:如果扩展函数在一个类里面,那么它的访问域是怎么样的呢?看如下代码:

class UploadManager private constructor() {
    //单例
    companion object {
        private var INSTANCE: UploadManager? = null
        fun getInstance(): UploadManager =
            INSTANCE ?: synchronized(this) {
                INSTANCE ?: UploadManager().also { INSTANCE = it }
            }
    }

    //扩展方法
    fun String.getStringAfterCut(regular: String, index: Int): String? {
        split(regular).let { list ->
            return list.getOrNull(index)
        }
    }

    //类的成员方法
    fun getFileName(filePath: String): String {
        return filePath.getStringAfterCut("/", 0) ?: ""
    }

}

反编译上面的代码,仅仅看扩展函数的部分:

public final class UploadManager {
   private static UploadManager INSTANCE;
   @NotNull
   public static final UploadManager.Companion Companion = new UploadManager.Companion((DefaultConstructorMarker)null);

   @Nullable
   public final String getStringAfterCut(@NotNull String $this$getStringAfterCut, @NotNull String regular, int index) {
      //...
      List var4 = StringsKt.split$default((CharSequence)$this$getStringAfterCut, new String[]{regular}, false, 0, 6, (Object)null);
      //...
      return (String)CollectionsKt.getOrNull(var4, index);
   }
   //...
 }
}

可以看到扩展函数变成了UploadManager类的成员方法,而不是扩展的String了,因为限制了扩展函数的接受者为String类型,而方法的调用者只能是UploadManager的单例对象,所以该扩展函数无法被外部调用,只能在UploadManager类中被其他成员方法调用。

故类内扩展的访问域仅限于该类当中的所有成员,以及被扩展类型的公开成员,这种方式定义的扩展仅能在该类当中使用。

五、扩展的限制

除了匿名类没有具体的接收类型,其他类都可以被扩展,包括普通类、单例类、密封类、枚举类等等。

扩展的主要用途是取代Java中的各种工具类。

1、Kotlin 扩展不是真正的类成员,因此它无法被它的子类重写。
2、Kotlin 扩展属性无法存储状态。
3、扩展的访问作用域仅限于定义处的成员和接收者类型的公开成员。

六、扩展的实战运用

1、关注点分离

我们看kotlin包中String.kt的代码:

package kotlin

public class String : Comparable<String>, CharSequence {
    companion object {}

    public operator fun plus(other: Any?): String

    public override val length: Int

    public override fun get(index: Int): Char

    public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence

    public override fun compareTo(other: String): Int
}

非常简单,只有String.kt类实现ComparableCharSequence重写的几个方法,那么我们平时使用的trimisEmpty等工具方法都去哪里了呢? 其实关于String的工具方法全都通过扩展的形式分离到了Strings.kt类中,这就是关注点分离,String.kt类只关注String自身,而扩展的工具方法全部由Strings.kt统一管理。

@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("StringsKt")

package kotlin.text

import kotlin.contracts.contract
import kotlin.jvm.JvmName

/**
 * Returns a copy of this string converted to upper case using the rules of the default locale.
 */
@Deprecated("Use uppercase() instead.", ReplaceWith("uppercase()"))
@DeprecatedSinceKotlin(warningSince = "1.5")
public expect fun String.toUpperCase(): String

/**
 * Returns a copy of this string converted to upper case using Unicode mapping rules of the invariant locale.
 *
 * This function supports one-to-many and many-to-one character mapping,
 * thus the length of the returned string can be different from the length of the original string.
 *
 * @sample samples.text.Strings.uppercase
 */
@SinceKotlin("1.5")
@WasExperimental(ExperimentalStdlibApi::class)
public expect fun String.uppercase(): String

/**
 * Returns a copy of this string converted to lower case using the rules of the default locale.
 */
@Deprecated("Use lowercase() instead.", ReplaceWith("lowercase()"))
@DeprecatedSinceKotlin(warningSince = "1.5")
public expect fun String.toLowerCase(): String

//...略...

2、提升可读性和开发效率

在Android开发中经常需要dp转px,如果写成工具类,那么就会这样使用:

//工具类调用
ConvertUtils.dp2px(15f)

但如果写成扩展属性的形式,那么调用就会变成

/**
 * 扩展属性:dp转化为px,返回Float值
 */
val Float.dp2px
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this,
        Resources.getSystem().displayMetrics
    )
//调用
8F.dp2px

会变得简单直观很多,且全局可调用,大大提高了可读性和开发效率。

个人学习笔记

参考了以下内容

06 | 扩展:你的能力边界到底在哪里?

作者:TimeFine
链接:https://juejin.cn/post/7193964032729022520

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值