扩展函数,就是从类的外部扩展出来的一个函数,这个函数看起来就像是类的成员函数一样。这里,我们就以JDK当中的String为例,来看看如何通过Kotlin的扩展特性,为它新增一个
lastElement()方法。
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(),是我们定义的扩展函数的名称。
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 编译器会将扩展函数转换成对应的静态方法,而扩展函数调用处的代码也会被转换成静态方法的调用。
扩展函数,是在类的外部为它定义一个新的成员方法;而扩展属性,则是在类的外部为它定义一个新的成员属性。
val String.lastElement: Char?
get() = if (this.isEmpty()) {
null
} else {
this[length - 1]
}
fun main() {
val str = "hi"
print(str.lastElement)
}
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();
}
}
几乎所有的类都可以被扩展,包括普通类、单例类、密封类、枚举类、伴生对象,甚至还包括第三方提供的 Java 类。
不包含匿名内部类
就是用来取代 Java 当中的各种工具类,比如 StringUtils、DateUtils 等等。
如下其中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")
}
}
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的逻辑。
//日志类
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 扩展主要有两个使用场景,分别是:关注点分离,优化代码架构;消灭模板代码,提高可读性和开发效率。