在 Kotlin 中使用类委托时,您可以覆盖成员。然而,关于授权的参考页说:
但请注意,以这种方式重写的成员不会从委托对象的成员中调用,委托对象只能访问其自己的接口成员实现。
我想重写委托对象的方法使用的属性,以便委托对象的方法调用此重写的属性。正如文档所述,使用关键字覆盖属性并override不能实现此目的。有没有办法可以实现这种行为?如果不是,这是否表明我应该使用继承?
这是一个代码示例:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
}
Run Code Online (Sandbox Code Playgroud)
此代码打印“BaseImpl:x = 10”,但我希望它打印“Message of Derived”。
Zoe*_*Zoe 11
问题出在生成的代码上:
public final class BaseImpl implements Base {
@NotNull
private final String message;
private final int x;
@NotNull
public String getMessage() {
return this.message;
}
public void print() {
String var1 = this.getMessage();
System.out.println(var1);
}
public final int getX() {
return this.x;
}
public BaseImpl(int x) {
this.x = x;
this.message = "BaseImpl: x = " + this.x;
}
}
public interface Base {
@NotNull
String getMessage();
void print();
}
public final class Derived implements Base {
@NotNull
private final String message;
// $FF: synthetic field
private final Base $$delegate_0;
@NotNull
public String getMessage() {
return this.message;
}
public Derived(@NotNull Base b) {
Intrinsics.checkParameterIsNotNull(b, "b");
super();
this.$$delegate_0 = b;
this.message = "Message of Derived";
}
public void print() {
this.$$delegate_0.print();
}
}
Run Code Online (Sandbox Code Playgroud)
我花了一段时间才发现它,但要点如下:
委托并不意味着扩展特定类型;而是意味着扩展特定类型。这意味着重写 Base(在本例中)接口,并将未重写的事件发送到委托类。这里有两件事需要密切关注:
public void print() {
this.$$delegate_0.print();
}
Run Code Online (Sandbox Code Playgroud)
和
public final class Derived implements Base {
Run Code Online (Sandbox Code Playgroud)
这是什么意思?这意味着Derived绝不覆盖BaseImpl。为了说明我的意思,请使用以下代码:
interface Base {
val message: String
fun print()
}
open class BaseImpl(val x: Int) : Base { // This needs to be `open` to override
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(x: Int) : BaseImpl(x) {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main(args: Array<String>) {
val derived = Derived(10)
derived.print()
}
Run Code Online (Sandbox Code Playgroud)
它将打印Message of Derived。为什么?因为现在你实际上覆盖了BaseImpl. 反编译代码,记住我之前告诉你要密切注意的两件事:
public final class Derived extends BaseImpl {
@NotNull
private final String message = "Message of Derived";
@NotNull
public String getMessage() {
return this.message;
}
public Derived(int x) {
super(x);
}
}
Run Code Online (Sandbox Code Playgroud)
您可能会看到 print 方法消失了;这是因为你没有覆盖它。另外,它现在extends BaseImpl代替了implements Base. 对该getMessage方法的任何调用(您可以在 print 方法中看到调用)都将“重定向”到重写的方法,而不是基类中的方法。
如果您在 Java 中执行此操作,而不是使用直接调用字段的 getter,则它将不起作用,因为您无法覆盖 Java 中的变量。它在 Kotlin 中开箱即用的原因是,正如您在反编译代码中看到的那样,它使用方法。
您想如何解决这个问题取决于您。如果您仍然想使用委托属性,则需要重写类中的 print 方法Derived。Derived因为,正如我之前提到的,除了有一个共同的父级之外,和之间没有实际的关系BaseImpl。但Derived不是 的孩子BaseImpl。
getMessage()因此,正如EpicPandaForce提到的那样,覆盖是行不通的。另外,通过这样做,override val message = ...您确实会生成一个压倒一切的 getter,正如我的第二个示例所示。
TL;DR: Base by b实现 Base,并将调用发送print到委托的BaseImpl. Derived不是 的子级BaseImpl,因此BaseImpl无法调用getMessage()中的 getter Derived,因此改为打印其自己的消息。
| 归档时间: |
|
| 查看次数: |
3613 次 |
| 最近记录: |