在Java中,常常会使用到诸如 StringUtil、DateUtil 等工具类,代码写起来比较长,并且调用方式不够简单直接。

在Java中,无法给String类添加自定义方法,因为String类是final,同时是JDK内置的基础类,不能修改。
一般的做法是开发一个StringUtil类,在里面封装相关的String操作方法,而不是修改或继承String类。

在Kotlin中,可以自由扩展任何类的方法和属性。
在不修改原类的情况下,Kotlin能一个类扩展新功能,同时无需继承该类。

扩展函数

String类扩展两个函数

/**
 * 给 String 扩展一个 firstChar() 函数
 */
fun String.firstChar(): String {
    if (this.isEmpty()) return ""
    return this[0].toString()
}

/**
 * 给 String 扩展一个 lastChar() 函数
 */
fun String.lastChar(): String {
    if (this.isEmpty()) return ""
    return this[this.length -1].toString()
}

// 测试扩展函数
fun main(args: Array<String>) {
    println("xyz".firstChar())  // 输出:x
    println("xyz".lastChar())  // 输出:y
    println("扩展函数".firstChar())  // 输出:扩
    println("扩展函数".lastChar())  // 输出:数
}

扩展的实现原理

扩展函数扩展属性的本质是以静态导入的方式来实现的。

在IDEA中查看Kotlin代码Bytecode 以及反编译成等价的Java代码:
Tools / Kotlin / Show Kotlin Bytecode ,可以查看Kotlin代码Bytecode;
然后选择Decompile按钮,就可以查看对应的Java代码了。

  • String 类的扩展函数 firstChar() 对应的Java代码:
   @NotNull
   public static final String firstChar(@NotNull String $this$firstChar) {
      Intrinsics.checkNotNullParameter($this$firstChar, "$this$firstChar");
      CharSequence var1 = (CharSequence)$this$firstChar;
      boolean var2 = false;
      return var1.length() == 0 ? "" : String.valueOf($this$firstChar.charAt(0));
   }

List类扩展一个函数

/**
 * 给 List 扩展一个 filterX() 函数
 *
 * predicate: (T) -> Boolean 是函数类型的参数
 */
fun <T> List<T>.filterX(predicate: (T) -> Boolean): MutableList<T> {
    val result = ArrayList<T>()
    this.forEach { // this 指向调用者对象
        if (predicate(it)) { // 满足判断条件
            result.add(it)
        }
    }
    return result
}

// 测试扩展函数
fun testListFilterX(): Unit {
    val list = listOf(1,2,3,4,5,6,7)
    val result = list.filterX { it % 3 == 0}
    println(result)  // 输出:[3, 6]
}

扩展属性

给 MutableList 扩展两个属性 firstElement和lastElement:

/**
 * 给 MutableList 扩展一个属性 firstElement
 */
var <T> MutableList<T>.firstElement: T?
    get() {
        return if (this.isNotEmpty()) this[0] else null
    }
    set(value) {
        if (value != null) {
            if (this.isNotEmpty()) {
                this[0] = value
            } else {
                add(value)
            }
        }
    }

/**
 * 给 MutableList 扩展一个属性 lastElement
 */
var <T> MutableList<T>.lastElement: T?
    get() {
        return if (this.isNotEmpty()) this[this.size -1] else null
    }
    set(value) {
        if (value != null) {
            if (this.isNotEmpty()) {
                this[this.size -1] = value
            } else {
                add(value)
            }
        }
    }

// 测试扩展属性
fun testMutableListExp(): Unit {
    val list1 = mutableListOf<Int>()
    println(list1)  // 输出:[]
    println(list1.firstElement)  // 输出:null
    println(list1.lastElement)  // 输出:null

    list1.apply {
        add(1)
        add(2)
        add(3)
        add(4)
        add(5)
    }
    println(list1)  // 输出:[1, 2, 3, 4, 5]
    println(list1.firstElement)  // 输出:1 . 调用了getter函数
    println(list1.lastElement)  // 输出:5

    list1.firstElement = -1     // 调用了setter函数
    list1.lastElement = 55
    println(list1)  // 输出:[-1, 2, 3, 4, 55]
}

扩展属性的实现原理

在IDEA中:选择 Tools / Kotlin / Show Kotlin Bytecode ,然后点击Decompile按钮,查看对应的Java代码。

MutableList 的扩展属性 firstElement 对应的Java代码:

   @Nullable
   public static final Object getFirstElement(@NotNull List $this$firstElement) {
      Intrinsics.checkNotNullParameter($this$firstElement, "$this$firstElement");
      Collection var1 = (Collection)$this$firstElement;
      boolean var2 = false;
      return !var1.isEmpty() ? $this$firstElement.get(0) : null;
   }

   public static final void setFirstElement(@NotNull List $this$firstElement, @Nullable Object value) {
      Intrinsics.checkNotNullParameter($this$firstElement, "$this$firstElement");
      if (value != null) {
         Collection var2 = (Collection)$this$firstElement;
         boolean var3 = false;
         if (!var2.isEmpty()) {
            $this$firstElement.set(0, value);
         } else {
            $this$firstElement.add(value);
         }
      }

   }

扩展中的this关键字

  • 在扩展函数中,以及带接收者的函数字面值中,this关键字代表调用函数时,在点号“.”之前的指定的对象实例。
  • 在类的成员函数中,this指向这个类的当前实例对象。
  • 如果this没有限定符,那么它指向包含当前代码的最内层范围。可以使用标签限定符,使this指向其他范围。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐