扩展方法在 Kotlin 中如何工作?
How does a extension method work in Kotlin?
在 Java 中你不能扩展一个最终的 class,但是在 Kotlin 中你可以为这样的最终 class 编写扩展方法。他们是如何让它在内部发挥作用的?我找不到任何解释 Kotlin 扩展方法内部工作原理的文章。
In Java you cannot extend a final class, but in Kotlin you can write
extension method to such final classes
我认为您假设扩展方法使用了继承,但事实并非如此。它是一种伪装在 kotlin 语法糖中的静态方法。例如看下面的简单扩展方法:
String.removeSpace(): String {
return this.replace(" ", "")
}
你可以认为它等同于下面的java代码。
static String removeSpace(String input) {
return input.replace(" ", "")
}
kotlin 在这里做的是它在顶部提供包装器,以便通过使用 this
您可以获得调用者的实例。通过将其与 Kotlin 将函数定义为第一个 class 公民的功能相结合,您可以编写如下优雅的内容:
fun String.removeSpace() = this.replace(" ", "")
如果您想知道 Kotlin 代码是如何转换成 Java 代码的,有一种方法可以查看。 IntelliJ 提供将 Kotlin 字节码反编译为 Java 代码。您可以找到它 Tools -> Kotlin -> Show Kotlin Bytecode
并点击 Decompile
。然后它将显示 Java 代码。
这是一个例子。
Kotlin
val Int.asByteArray get() =
byteArrayOf(
(this shr 24).toByte(),
(this shr 16).toByte(),
(this shr 8).toByte(),
this.toByte())
val Int.asHex get() = this.asByteArray.asHexUpper
@ExperimentalUnsignedTypes
val ByteArray.asInt get() =
((this[0].toUInt() and 0xFFu) shl 24) or
((this[1].toUInt() and 0xFFu) shl 16) or
((this[2].toUInt() and 0xFFu) shl 8) or
(this[3].toUInt() and 0xFFu)
Java
public final class IntExtensionKt {
@NotNull
public static final byte[] getAsByteArray(int $this$asByteArray) {
return new byte[]{(byte)($this$asByteArray >> 24), (byte)($this$asByteArray >> 16), (byte)($this$asByteArray >> 8), (byte)$this$asByteArray};
}
@NotNull
public static final String getAsHex(int $this$asHex) {
return HexExtensionKt.getAsHexUpper(getAsByteArray($this$asHex));
}
/** @deprecated */
// $FF: synthetic method
@ExperimentalUnsignedTypes
public static void asInt$annotations(byte[] var0) {
}
public static final int getAsInt(@NotNull byte[] $this$asInt) {
Intrinsics.checkParameterIsNotNull($this$asInt, "$this$asInt");
byte var1 = $this$asInt[0];
boolean var2 = false;
int var5 = UInt.constructor-impl(var1);
short var6 = 255;
boolean var3 = false;
var5 = UInt.constructor-impl(var5 & var6);
byte var7 = 24;
var3 = false;
var5 = UInt.constructor-impl(var5 << var7);
byte var8 = $this$asInt[1];
var3 = false;
int var9 = UInt.constructor-impl(var8);
short var10 = 255;
boolean var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
byte var11 = 16;
var4 = false;
var9 = UInt.constructor-impl(var9 << var11);
var3 = false;
var5 = UInt.constructor-impl(var5 | var9);
var8 = $this$asInt[2];
var3 = false;
var9 = UInt.constructor-impl(var8);
var10 = 255;
var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
var11 = 8;
var4 = false;
var9 = UInt.constructor-impl(var9 << var11);
var3 = false;
var5 = UInt.constructor-impl(var5 | var9);
var8 = $this$asInt[3];
var3 = false;
var9 = UInt.constructor-impl(var8);
var10 = 255;
var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
var3 = false;
return UInt.constructor-impl(var5 | var9);
}
}
如您所见,Kotlin 扩展方法在 Java 中变成了 public final static
中的方法 final class
。
在 Java 中你不能扩展一个最终的 class,但是在 Kotlin 中你可以为这样的最终 class 编写扩展方法。他们是如何让它在内部发挥作用的?我找不到任何解释 Kotlin 扩展方法内部工作原理的文章。
In Java you cannot extend a final class, but in Kotlin you can write extension method to such final classes
我认为您假设扩展方法使用了继承,但事实并非如此。它是一种伪装在 kotlin 语法糖中的静态方法。例如看下面的简单扩展方法:
String.removeSpace(): String {
return this.replace(" ", "")
}
你可以认为它等同于下面的java代码。
static String removeSpace(String input) {
return input.replace(" ", "")
}
kotlin 在这里做的是它在顶部提供包装器,以便通过使用 this
您可以获得调用者的实例。通过将其与 Kotlin 将函数定义为第一个 class 公民的功能相结合,您可以编写如下优雅的内容:
fun String.removeSpace() = this.replace(" ", "")
如果您想知道 Kotlin 代码是如何转换成 Java 代码的,有一种方法可以查看。 IntelliJ 提供将 Kotlin 字节码反编译为 Java 代码。您可以找到它 Tools -> Kotlin -> Show Kotlin Bytecode
并点击 Decompile
。然后它将显示 Java 代码。
这是一个例子。
Kotlin
val Int.asByteArray get() =
byteArrayOf(
(this shr 24).toByte(),
(this shr 16).toByte(),
(this shr 8).toByte(),
this.toByte())
val Int.asHex get() = this.asByteArray.asHexUpper
@ExperimentalUnsignedTypes
val ByteArray.asInt get() =
((this[0].toUInt() and 0xFFu) shl 24) or
((this[1].toUInt() and 0xFFu) shl 16) or
((this[2].toUInt() and 0xFFu) shl 8) or
(this[3].toUInt() and 0xFFu)
Java
public final class IntExtensionKt {
@NotNull
public static final byte[] getAsByteArray(int $this$asByteArray) {
return new byte[]{(byte)($this$asByteArray >> 24), (byte)($this$asByteArray >> 16), (byte)($this$asByteArray >> 8), (byte)$this$asByteArray};
}
@NotNull
public static final String getAsHex(int $this$asHex) {
return HexExtensionKt.getAsHexUpper(getAsByteArray($this$asHex));
}
/** @deprecated */
// $FF: synthetic method
@ExperimentalUnsignedTypes
public static void asInt$annotations(byte[] var0) {
}
public static final int getAsInt(@NotNull byte[] $this$asInt) {
Intrinsics.checkParameterIsNotNull($this$asInt, "$this$asInt");
byte var1 = $this$asInt[0];
boolean var2 = false;
int var5 = UInt.constructor-impl(var1);
short var6 = 255;
boolean var3 = false;
var5 = UInt.constructor-impl(var5 & var6);
byte var7 = 24;
var3 = false;
var5 = UInt.constructor-impl(var5 << var7);
byte var8 = $this$asInt[1];
var3 = false;
int var9 = UInt.constructor-impl(var8);
short var10 = 255;
boolean var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
byte var11 = 16;
var4 = false;
var9 = UInt.constructor-impl(var9 << var11);
var3 = false;
var5 = UInt.constructor-impl(var5 | var9);
var8 = $this$asInt[2];
var3 = false;
var9 = UInt.constructor-impl(var8);
var10 = 255;
var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
var11 = 8;
var4 = false;
var9 = UInt.constructor-impl(var9 << var11);
var3 = false;
var5 = UInt.constructor-impl(var5 | var9);
var8 = $this$asInt[3];
var3 = false;
var9 = UInt.constructor-impl(var8);
var10 = 255;
var4 = false;
var9 = UInt.constructor-impl(var9 & var10);
var3 = false;
return UInt.constructor-impl(var5 | var9);
}
}
如您所见,Kotlin 扩展方法在 Java 中变成了 public final static
中的方法 final class
。