Kotlin扩展函数和扩展属性 使用及优化场景分析

1.扩展函数与扩展属性基础

1.1 什么是扩展函数

扩展函数,就是从类的外部扩展出来的一个函数,这个函数看起来就像是类的成员函数一样。这里,我们就以JDK当中的String为例,来看看如何通过Kotlin的扩展特性,为它新增一个
lastElement()方法。

扩展函数Case
fun String.lastElement(): Char?{
    if(this.isEmpty()){
        return null
    }

    return this[length-1]
}

fun main(){
    val str = "hi"
    print(str.lastElement())
}

“String.”,代表我们的扩展函数是为 String 这个类定义的。在 Kotlin 当中,它
有一个名字,叫做接收者(Receiver),也就是扩展函数的接收方。
lastElement(),是我们定义的扩展函数的名称。

扩展函数反编译的Java实现
package com.heancoder.jetpack.case6;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 9, 0},
   k = 2,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\f\n\u0002\u0010\u000e\n\u0002\b\u0002\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a\u0011\u0010\u0002\u001a\u0004\u0018\u00010\u0003*\u00020\u0004¢\u0006\u0002\u0010\u0005¨\u0006\u0006"},
   d2 = {"main", "", "lastElement", "", "", "(Ljava/lang/String;)Ljava/lang/Character;", "app_debug"}
)
public final class Test6Kt {
//Kotlin 编写的扩展函数调用代码,最终会变成静态方法的调用。
   @Nullable
   public static final Character lastElement(@NotNull String $this$lastElement) {
      Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
      CharSequence var1 = (CharSequence)$this$lastElement;
      return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
   }

   public static final void main() {
      String str = "hi";
      Character var1 = lastElement(str);
      System.out.print(var1);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

结论:
Kotlin 编译器会将扩展函数转换成对应的静态方法,而扩展函数调用处的代码也会被转换成静态方法的调用。

1.2 什么是扩展属性

扩展函数,是在类的外部为它定义一个新的成员方法;而扩展属性,则是在类的外部为它定义一个新的成员属性。

扩展属性Case
val String.lastElement: Char?
    get() = if (this.isEmpty()) {
        null
    } else {
        this[length - 1]
    }

fun main() {
    val str = "hi"
    print(str.lastElement)
}
扩展属性反编译的Java实现
package com.heancoder.jetpack.case6;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 9, 0},
   k = 2,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\f\n\u0002\u0010\u000e\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0005\u001a\u00020\u0006\"\u0017\u0010\u0000\u001a\u0004\u0018\u00010\u0001*\u00020\u00028F¢\u0006\u0006\u001a\u0004\b\u0003\u0010\u0004¨\u0006\u0007"},
   d2 = {"lastElement", "", "", "getLastElement", "(Ljava/lang/String;)Ljava/lang/Character;", "main", "", "app_debug"}
)
public final class Test6Kt {
   @Nullable
   public static final Character getLastElement(@NotNull String $this$lastElement) {
      Intrinsics.checkNotNullParameter($this$lastElement, "$this$lastElement");
      CharSequence var1 = (CharSequence)$this$lastElement;
      return var1.length() == 0 ? null : $this$lastElement.charAt($this$lastElement.length() - 1);
   }

   public static final void main() {
      String str = "hi";
      Character var1 = getLastElement(str);
      System.out.print(var1);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}

2.使用场景

几乎所有的类都可以被扩展,包括普通类、单例类、密封类、枚举类、伴生对象,甚至还包括第三方提供的 Java 类。
不包含匿名内部类

主要用途

就是用来取代 Java 当中的各种工具类,比如 StringUtils、DateUtils 等等。

优势

开发工具可以在编写代码的时候智能提示
Kotlin扩展函数和扩展属性 使用及优化场景分析_第1张图片

不能使用场景
  1. Kotlin 扩展不是真正的类成员,因此它无法被它的子类重写。
  2. 扩展属性无法存储状态
  3. 扩展的访问作用域仅限:A:定义处的成员;B:接收者类型的公开成员
关于作用域详解

如下其中this[length - 1] 中的length 是String的public 属性, private、protected是无法访问的。

val String.lastElement: Char?
    get() = if (this.isEmpty()) {
        null
    } else {
        this[length - 1]
    }

扩展函数可以调用类内部私有属性

package com.heancoder.jetpack.case6

open class Person{
    var name: String = ""
    var age: Int = 0
}

class Helper{
    private fun run(){
        print("run")
    }

    fun Person.state(){
        run()
    }

    fun test(){
        var p = Person()
        p.state()
        print("run 2")
    }
}

3.扩展函数与扩展属性实战

3.1 Strings.kt 对 String 类的扩展函数应用

String,kt

/*
 * Copyright 2010-2016 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kotlin

/**
 * The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
 * implemented as instances of this class.
 */
public class String : Comparable<String>, CharSequence {
    companion object {}
    
    /**
     * Returns a string obtained by concatenating this string with the string representation of the given [other] object.
     */
    @kotlin.internal.IntrinsicConstEvaluation
    public operator fun plus(other: Any?): String

    @kotlin.internal.IntrinsicConstEvaluation
    public override val length: Int

    /**
     * Returns the character of this string at the specified [index].
     *
     * If the [index] is out of bounds of this string, throws an [IndexOutOfBoundsException] except in Kotlin/JS
     * where the behavior is unspecified.
     */
    @kotlin.internal.IntrinsicConstEvaluation
    public override fun get(index: Int): Char

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

    @kotlin.internal.IntrinsicConstEvaluation
    public override fun compareTo(other: String): Int

    @kotlin.internal.IntrinsicConstEvaluation
    public override fun equals(other: Any?): Boolean

    @kotlin.internal.IntrinsicConstEvaluation
    public override fun toString(): String
}

Strings.kt 部分源码

@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

结论是:String类关注subSequence 等本身的逻辑,Strings作为String的扩展关注操作符 uppercase、toLowerCase、capitalize的逻辑。

3.2 项目应用场景概括

日志类工具类快捷输出

//日志类
fun Any?.printLog(tag: String = "DEBUG"){
    Log.d(tag, toString())
}

//使用
fun test(){
  val str = "hello"
  str.printLog()
}
//Toast
import android.app.Activity
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment

fun Fragment.toast(message: String) {
    Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
}

fun Fragment.toast(@StringRes message: Int) {
    Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
}

fun Activity.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

fun Activity.toast(@StringRes message: Int) {
    Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
//snackbar 顶部展示信息
import android.view.View
import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar

fun View.snackbar(message: String, duration: Int = Snackbar.LENGTH_LONG) {
    Snackbar.make(this, message, duration).show()
}

fun View.snackbar(@StringRes message: Int, duration: Int = Snackbar.LENGTH_LONG) {
    Snackbar.make(this, message, duration).show()
}
//键盘处理
import android.app.Activity
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment

fun Activity.hideKeyboard() {
    val imm: InputMethodManager =
        getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    val view = currentFocus ?: View(this)
    imm.hideSoftInputFromWindow(view.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
}

fun Fragment.hideKeyboard() {
    activity?.apply {
        val imm: InputMethodManager =
            getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
        val view = currentFocus ?: View(this)
        imm.hideSoftInputFromWindow(view.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }
}

// px2dp  使用-》  12.px
import android.content.res.Resources

// Convert px to dp
val Int.dp: Int
    get() = (this / Resources.getSystem().displayMetrics.density).toInt()

//Convert dp to px
val Int.px: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()
fun String.toDate(format: String = "yyyy-MM-dd HH:mm:ss"): Date? {
    val dateFormatter = SimpleDateFormat(format, Locale.getDefault())
    return dateFormatter.parse(this)
}

fun Date.toStringFormat(format: String = "yyyy-MM-dd HH:mm:ss"): String {
    val dateFormatter = SimpleDateFormat(format, Locale.getDefault())
    return dateFormatter.format(this)
}

总结:
Kotlin扩展包含扩展函数和扩展属性。定义扩展的方式,只是比普通函数、属性多了一个“扩展接收者”而已。
作用域:分为顶层扩展和类内扩展。
本质:扩展函数和扩展属性,它们都是 Java 静态方法,与 Java 当中的工具类别无二致。

扩展优势 :IDE 可以为我们提供代码补全功能。
能力角度:Kotlin 扩展一共有三个限制,分别是:扩展无法被重写;扩展属性无法存储状态;扩展的作用域有限,无法访问私有成员。
场景的角度: Kotlin 扩展主要有两个使用场景,分别是:关注点分离,优化代码架构;消灭模板代码,提高可读性和开发效率。
Kotlin扩展函数和扩展属性 使用及优化场景分析_第2张图片

你可能感兴趣的:(Kotlin,Android精华基础,kotlin,开发语言,android)