Kotlin覆盖抽象的val行为,对象vs类

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

所以我有四个class:

主要活动:

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") } 

一类:

 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{}被执行之后才会被init{} 。 但后来我看到发生了什么事,当我创建一个单身人士。 testUriString的值在init设置。 这是为什么? 这是一个错误? 什么是单身人士的预期行为和重写val?

我试图搜索文档,但在文档中没有find任何关于此的信息。

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

  • 当一个类使用后台字段覆盖一个属性时,在派生类中有一个单独的实例字段,重写的getter返回该字段的值。

    所以,当你从超类的构造函数中访问属性的时候,就是被调用的被重写的getter,它返回字段的null值(在那个时候它是未初始化的,因为超类构造函数是在类自己初始化之前调用的逻辑)。

  • 相反,当你定义一个object覆盖一个类时,那么底层的类Object将其后台字段定义为JVM static字段。

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

    现在有趣的部分是:当JVM加载类Object类时,即使在执行ObjectPUTSTATIC指令之前,由常量值初始化的静态字段已经包含这些值。

    如果将testString初始值设定项更改为非常数值,则不会在被访问时被初始化,例如override val testString: String = "test".also { println(it) }

    这里有一个这样的单身人士的字节码与我做了几个标记的要点 。 请注意,在将值放入Object之前,该字段将由抽象类的构造函数访问。

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