美文网首页
Kotlin-扩展(Extension)的能力边界在哪?

Kotlin-扩展(Extension)的能力边界在哪?

作者: 大虾啊啊啊 | 来源:发表于2022-09-20 22:17 被阅读0次

    1、扩展函数

    我们对String定义一个扩展函数

    //对String 增加扩展函数lastElement
    //直接定义在kotlin文件里,称之为顶层扩展、
    fun String.lastElement(): Char? {
        if (this.isEmpty()) {
            return null
        }
        return this[length - 1]
    }
    fun main() {
        val a = "Hello World"
        println(a.lastElement())
    }
    
    

    扩展函数我们定义在kotlin文件中,称之为顶层扩展,任何地方都可以使用,转成Java代码看实现

    
    public final class ExtentionKt {
       @Nullable
       public static final Character lastElement(@NotNull String $this$lastElement) {
          Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
          CharSequence var1 = (CharSequence)$this$lastElement;
          boolean var2 = false;
          return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
       }
    
       public static final void main() {
          String a = "Hello World";
          Character var1 = lastElement(a);
          boolean var2 = false;
          System.out.println(var1);
       }
    
    }
    
    

    其实实现很简单,定义了一个在ExtentionKt 类中定义了一个lastElement静态方法并传入String对象。在调用的时候,其实就是传入了我们的字符串a即可。然后再算出最后一个字符。
    所以 Kotlin 编译器会将扩展函数转换成对应的静态方法,而扩展函数调用处的代码也会被转换成静态方法的调用。

    2、扩展属性

    //对String 增加扩展函数lastElement
    //直接定义在kotlin文件里,称之为顶层扩展、
    fun String.lastElement(): Char? {
        if (this.isEmpty()) {
            return null
        }
        return this[length - 1]
    }
    //定义扩展属性
    val String.firstElement: Char?
        get() = if (isEmpty()) null else this[0]
    fun main() {
        val a = "Hello World"
        println(a.lastElement())
        println(a.firstElement)
    
    }
    
    

    转成Java代码看实现

    public final class ExtentionKt {
       @Nullable
       public static final Character lastElement(@NotNull String $this$lastElement) {
          Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
          CharSequence var1 = (CharSequence)$this$lastElement;
          boolean var2 = false;
          return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
       }
    
       @Nullable
       public static final Character getFirstElement(@NotNull String $this$firstElement) {
          Intrinsics.checkNotNullParameter($this$firstElement, "$this$firstElement");
          CharSequence var1 = (CharSequence)$this$firstElement;
          boolean var2 = false;
          return var1.length() == 0 ? null : $this$firstElement.charAt(0);
       }
    
       public static final void main() {
          String a = "Hello World";
          Character var1 = lastElement(a);
          boolean var2 = false;
          System.out.println(var1);
          var1 = getFirstElement(a);
          var2 = false;
          System.out.println(var1);
       }
       // $FF: synthetic method
       public static void main(String[] var0) {
          main();
       }
    }
    

    从Java代码中我们可以看到,最终也是转成一个静态的getFirstElement方法,其实我们很容易理解,当我们的属性firstElement 设置成val 非私有的时候,对应的Java代码就会默认持有getter方法,也就是我们的getFirstElement。当我们定义了扩展属性,那么对应的Java代码就会转成相应的静态的getter方法。

    3、扩展的能力边界?

    从以上我们转成Java便可以知道,kotlin中不管的是扩展函数还是扩展属性对应都是Java中的静态方法,也就是主要用于替代Java 中的各种工具类。例如我们的工具类主要是操作字符串,那么我们就对String类进行扩展

    扩展能做什么?

    在Kotlin中几乎所有的类都可以扩展、主要用途是取代Java中的各种工具类,StringUtils等等。当我们对某个类进行扩展成员的时候,扩展的成员实际上不是真正的成员,但是我们在编译器中有智能的提示,这样一来就方便开发。

    扩展不能做什么?

    • 扩展不是真正的成员,无法被子类重写
    • 扩展属性无法存储状态
      如:
    //定义扩展属性
    val String.firstElement: Char?
        get() = if (isEmpty()) null else this[0]
    

    其实很容易理解,扩展属性转成Java代码其实就对应了静态的getter方法而已,具体的值由getter方法返回值决定

    • 扩展的访问作用域
      (1)定义处的成员
      (2)接受者类型的公开成员
      如:
    private val name = "dog";
    //定义扩展属性
    val String.firstElement: Char?
        get() = if (isEmpty()) {
            null
        } else {
            println(name)
            println(this.length)
            this[0]
        }
    

    name成员虽然是私有的,但是定义在和扩展属性firstElement同一个文件,因此是可以访问到name 的,否则不能
    其次就是访问被扩展类型的公开成员。如以上的例子访问String的length 通过this.length,如果length是私有的则不能访问。
    以上我们定义的是顶层扩展,如果我们在某个类中进行扩展呢?

    class Pig {
        //对String 增加扩展函数lastElement
    //直接定义在kotlin文件里,称之为顶层扩展、
        fun String.lastElement(): Char? {
            if (this.isEmpty()) {
                return null
            }
            return this[length - 1]
        }
    
        private val name = "dog";
        //定义扩展属性
        val String.firstElement: Char?
            get() = if (isEmpty()) {
                null
            } else {
                println(name)
                println(this.length)
                this[0]
            }
    
        fun testDemo(){
            val a = "Hello World"
            println(a.lastElement())
            println(a.firstElement)
        }
    }
    
    

    转成Java代码

    
    public final class Pig {
       private final String name = "dog";
    
       @Nullable
       public final Character lastElement(@NotNull String $this$lastElement) {
          Intrinsics.checkParameterIsNotNull($this$lastElement, "$this$lastElement");
          CharSequence var2 = (CharSequence)$this$lastElement;
          boolean var3 = false;
          return var2.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
       }
    
       @Nullable
       public final Character getFirstElement(@NotNull String $this$firstElement) {
          Intrinsics.checkParameterIsNotNull($this$firstElement, "$this$firstElement");
          CharSequence var2 = (CharSequence)$this$firstElement;
          boolean var3 = false;
          Character var10000;
          if (var2.length() == 0) {
             var10000 = null;
          } else {
             String var4 = this.name;
             var3 = false;
             System.out.println(var4);
             int var5 = $this$firstElement.length();
             var3 = false;
             System.out.println(var5);
             var10000 = $this$firstElement.charAt(0);
          }
    
          return var10000;
       }
    
       public final void testDemo() {
          String a = "Hello World";
          Character var2 = this.lastElement(a);
          boolean var3 = false;
          System.out.println(var2);
          var2 = this.getFirstElement(a);
          var3 = false;
          System.out.println(var2);
       }
    }
    
    

    其实非常容易了理解,当扩展成员定义在类中,那么只能在类中访问
    因此可知:
    如果是顶层扩展,是可以被全局使用的,扩展成员的访问作用域限于所在文件的所有成员,以及被扩展类型的公开成员
    如果是在类中扩展,那么只能在类中使用,扩展成员的访问作用域为该类的成员,以及被扩展类型的公开成员

    4、实战与思考

    以上我们分析了扩展函数常用于替代Java中的各种工具类,仅此而已吗?
    我们来看下Kotlin中的String

    public class String : Comparable<String>, CharSequence {
        companion object {}
        
        public override fun get(index: Int): Char
    
        public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence
    
        public override fun compareTo(other: String): Int
    }
    

    我们看到String中只有几个核心方法,而平时我们使用到的这么多API,都在Strings这个顶层扩展文件中,


    image.png

    由此可知,在优化我们的项目架构的时候,我们可以将核心方法写在类中,非核心方法我们可以通过扩展的方式实现分离。

    第二就是对于SDK中的代码,我们在使用的时候,总是要写很多公共的模板代码,如果我们使用Java可以想到把他封装成一个静态工具类,在Kotlin我们便可以通过扩展成员的方式进行封装。虽然说扩展的方式转换成Java代码也是通过静态方法进行封装,但是对于我们开发中却非常方便,开发中编译器就类似把扩展成员当做真正的成员,智能提示。
    如下代码:

    当我们要设置View的margin的时候,总要写很多模板代码

        val param = textView?.layoutParams as ViewGroup.MarginLayoutParams
        param.bottomMargin = 10
        param.topMargin = 10
        param.marginStart = 10
        param.marginEnd = 10
        textView.layoutParams = param
    

    那么我们便可以通过扩展的方式,使得代码更加方便

    fun View.setMargin(left: Int, top: Int, right: Int, bottom: Int) {
        ( layoutParams as ViewGroup.MarginLayoutParams).let { 
            it.bottomMargin = bottom
            it.topMargin = top
            it.marginStart = left
            it.marginEnd = right
        }
    }
    fun main() {
       val textView: TextView? = null
        textView?.setMargin(10, 20, 30, 50)
    
        val button: Button? = null
        button?.setMargin(10, 20, 30, 50)
    }
    

    这样针对所有的View我们都可以使用。

    5、小结

    • 扩展我们可以用于替代Java中的一些工具类
    • 我们可以将核心方法写在类中,非核心成员写在扩展成员中
    • 当我们调用一些外部SDK中的函数总是要写很多模板方法,我们便可以对SDK类进行扩展
    • 扩展成员不是真正的成员,因此不能继承,不能保存数据,其底层最终也是封装成了静态的方式实现
    • 顶层扩展在任何地方都可以使用,类中扩展只能在类中使用,因此我们一般使用底层扩展

    相关文章

      网友评论

          本文标题:Kotlin-扩展(Extension)的能力边界在哪?

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