Kotlin覆盖抽象val行为,对象与类

时间:2017-12-11 16:42:12

标签: android object kotlin singleton abstract

我刚刚开始使用并开始搞乱抽象类,覆盖val和singeltons。但是,我刚刚遇到了一个非常奇怪的行为。我的目标是创建一个抽象类,然后创建几个扩展该抽象类的singeltons。由于我想要某些变量,我创建了抽象val,然后可以在子类中重写(而不是通过构造函数传递它们)。

所以我有4节课:

MainActivity:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val instance = Instance()
        Log.d("MainActivity", "instance randObject: ${instance.randObject}")
        Log.d("MainActivity", "instance randObject: ${instance.randObject.myProp}")
        Log.d("MainActivity", "instance randObject: ${instance.randObject.myProp2}")

        Log.d("MainActivity", "singleton randObject: ${Object.randObject}")
        Log.d("MainActivity", "singleton randObject: ${Object.randObject.myProp}")
        Log.d("MainActivity", "singleton randObject: ${Object.randObject.myProp2}")    
    }
}

实例

class Instance: AClass(){
    override val testString: String = "test"
    override val testUriString: String = "https://www.google.se"
    override val testUri: Uri = Uri.parse(testUriString)!!
    override val randObject: RandomObject = RandomObject("Herp")
}

对象

object Object : AClass(){
    override val testString: String = "test"
    override val testUriString: String = "https://www.google.se"
    override val testUri: Uri = Uri.parse(testUriString)!!
    override val randObject: RandomObject = RandomObject("Herp")
}

ACLASS:

abstract class AClass{
    abstract val testString: String
    abstract val testUriString: String
    abstract val testUri: Uri
    abstract val randObject: RandomObject

    init {
        Log.d("AClass", "testString: $testString")
        Log.d("AClass", "testUriString: $testUriString")
        Log.d("AClass", "testUri: $testUri")
        Log.d("AClass", "randObject: $randObject")
    }
}

输出:

D/AClass: testString: null
D/AClass: testUriString: null
D/AClass: testUri: null
D/AClass: randObject: null
D/MainActivity: instance randObject: com.technocreatives.abstracttest.RandomObject@4455b26
D/MainActivity: instance randObject: derp
D/MainActivity: instance randObject: Herp

D/AClass: testString: test
D/AClass: testUriString: https://www.google.se
D/AClass: testUri: null
D/AClass: randObject: null
D/MainActivity: singleton randObject: com.technocreatives.abstracttest.RandomObject@8b19367
D/MainActivity: singleton randObject: derp
D/MainActivity: singleton randObject: Herp

在此之后,我意识到在init{}被执行之后,被覆盖的可能不会被初始化。但后来我看到了当我创建一个单身人物时发生的事情。值testUriStringinit中设置。为什么会这样?这是一个错误吗?单身和覆盖val的预期行为是什么?

我尝试搜索文档但未在文档中找到有关此内容的任何信息。

1 个答案:

答案 0 :(得分:3)

您观察到的行为差异是由为类和对象中的属性生成支持字段的方式以及如何初始化这些属性引起的。

  • 当类使用支持字段覆盖属性时,在引擎类中,派生类中有一个单独的实例字段,被覆盖的getter返回该字段的值。

    因此,当您从超类构造函数内部访问该属性时,它将被调用的重写的getter返回该字段的null值(此时它未初始化,因为在类“自己的初始化逻辑”之前调用超级构造函数。

  • 相反,当您定义覆盖类的object时,基础类Object的后备字段定义为JVM static字段。

    Object类也有一个实例(它甚至可以在Java中以Object.INSTANCE形式访问),并且此实例在某个时间点初始化并调用超级构造函数。

    现在有趣的部分:当JVM加载类Object类时,由常量值初始化的静态字段已经包含这些值,甚至在PUTSTATIC指令之前{ {1}} Object已被执行。

    如果您将<clinit>初始化程序更改为非常量值,则在访问它时不会对其进行初始化。 testString

    Here's a gist带有这样一个单例的字节码,带有几个标记。 请注意,在将值放入override val testString: String = "test".also { println(it) }的{​​{1}}之前,该字段由抽象类的构造函数访问。

我不确定它实际上是一个错误,但至少行为是不一致的。我已向问题跟踪器报告此不一致情况:https://youtrack.jetbrains.com/issue/KT-21764

相关问题