1溪椎、擴展函數(shù)
我們對String定義一個擴展函數(shù)
//對String 增加擴展函數(shù)lastElement
//直接定義在kotlin文件里普舆,稱之為頂層擴展、
fun String.lastElement(): Char? {
if (this.isEmpty()) {
return null
}
return this[length - 1]
}
fun main() {
val a = "Hello World"
println(a.lastElement())
}
擴展函數(shù)我們定義在kotlin文件中校读,稱之為頂層擴展沼侣,任何地方都可以使用,轉成Java代碼看實現(xiàn)
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);
}
}
其實實現(xiàn)很簡單歉秫,定義了一個在ExtentionKt 類中定義了一個lastElement靜態(tài)方法并傳入String對象蛾洛。在調用的時候,其實就是傳入了我們的字符串a即可端考。然后再算出最后一個字符雅潭。
所以 Kotlin 編譯器會將擴展函數(shù)轉換成對應的靜態(tài)方法,而擴展函數(shù)調用處的代碼也會被轉換成靜態(tài)方法的調用却特。
2扶供、擴展屬性
//對String 增加擴展函數(shù)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代碼看實現(xiàn)
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代碼中我們可以看到椿浓,最終也是轉成一個靜態(tài)的getFirstElement方法,其實我們很容易理解闽晦,當我們的屬性firstElement 設置成val 非私有的時候扳碍,對應的Java代碼就會默認持有getter方法,也就是我們的getFirstElement仙蛉。當我們定義了擴展屬性笋敞,那么對應的Java代碼就會轉成相應的靜態(tài)的getter方法。
3荠瘪、擴展的能力邊界夯巷?
從以上我們轉成Java便可以知道,kotlin中不管的是擴展函數(shù)還是擴展屬性對應都是Java中的靜態(tài)方法哀墓,也就是主要用于替代Java 中的各種工具類趁餐。例如我們的工具類主要是操作字符串,那么我們就對String類進行擴展
擴展能做什么篮绰?
在Kotlin中幾乎所有的類都可以擴展后雷、主要用途是取代Java中的各種工具類,StringUtils等等。當我們對某個類進行擴展成員的時候臀突,擴展的成員實際上不是真正的成員勉抓,但是我們在編譯器中有智能的提示,這樣一來就方便開發(fā)候学。
擴展不能做什么琳状?
- 擴展不是真正的成員,無法被子類重寫
- 擴展屬性無法存儲狀態(tài)
如:
//定義擴展屬性
val String.firstElement: Char?
get() = if (isEmpty()) null else this[0]
其實很容易理解盒齿,擴展屬性轉成Java代碼其實就對應了靜態(tài)的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 增加擴展函數(shù)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禀酱、實戰(zhàn)與思考
以上我們分析了擴展函數(shù)常用于替代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這個頂層擴展文件中,
由此可知浩聋,在優(yōu)化我們的項目架構的時候观蜗,我們可以將核心方法寫在類中,非核心方法我們可以通過擴展的方式實現(xiàn)分離衣洁。
第二就是對于SDK中的代碼墓捻,我們在使用的時候,總是要寫很多公共的模板代碼,如果我們使用Java可以想到把他封裝成一個靜態(tài)工具類砖第,在Kotlin我們便可以通過擴展成員的方式進行封裝撤卢。雖然說擴展的方式轉換成Java代碼也是通過靜態(tài)方法進行封裝,但是對于我們開發(fā)中卻非常方便梧兼,開發(fā)中編譯器就類似把擴展成員當做真正的成員放吩,智能提示。
如下代碼:
當我們要設置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中的函數(shù)總是要寫很多模板方法,我們便可以對SDK類進行擴展
- 擴展成員不是真正的成員颜骤,因此不能繼承唧喉,不能保存數(shù)據(jù),其底層最終也是封裝成了靜態(tài)的方式實現(xiàn)
- 頂層擴展在任何地方都可以使用忍抽,類中擴展只能在類中使用八孝,因此我們一般使用底層擴展