当前位置: 首页 > 知识库问答 >
问题:

静态编程语言覆盖抽象val行为,对象vs类

高朝明
2023-03-14

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

所以我上了4节课:

主要活动:

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{} 之后才会初始化。但后来我看到了当我创建一个单例时发生了什么。值测试字符串在初始化中设置。为什么会这样呢?这是一个错误吗?单例和覆盖 val 的预期行为是什么?

我试着搜索文档,但是没有在文档中找到任何关于这个的信息。

共有1个答案

范翰海
2023-03-14

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

>

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

    因此,当您从超类构造函数内部访问属性时,调用的是被覆盖的getter,它返回字段的< code>null值(此时它未初始化,因为超构造函数是在类自己的初始化逻辑之前调用的)。

    相反,当您定义一个覆盖类的对象时,基础类 Object 会将其支持字段定义为 JVM 静态字段。

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

    现在有趣的部分是:当类<code>对象<code>类被JVM加载时,其由常量值初始化的静态字段已经包含这些值,甚至在<code>Object<code>的<code>中<code>PUTSTATIC<code>指令之前

    如果您将testString初始化程序更改为非常量值,则在访问时不会初始化它,例如覆盖val testString: String="test".也{println(it)}

    < sup >这里有一个要点,是我做的带有一些标记的单例字节码。请注意,在将值放入< code >之前,该字段是由抽象类的构造函数访问的

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

  •  类似资料:
    • 我是Kotlin开发的新手,我不知道如何处理这个问题。我将以下Kotlin数据类映射到MongoDB集合(Spring data MongoDB): 我想覆盖默认的 id 获取器并返回一个字符串而不是对象 Id。似乎“id”字段名称无法更改,因为我收到消息“不允许自定义id属性的字段名称!自定义名称不会被考虑!“,因此我无法使用始终建议的_id解决方案。 如何才能做到这一点?我错过了什么吗?

    • 我为我的RecyclerView设置了一个ItemClickLister,如下所示: 使用我翻译成Kotlin的ItemClickSupport库。 我在(第2行)上收到一个错误,它说: 对象必须声明为抽象或实现抽象成员。 我真的是静态编程语言的新手,我在SO上也找不到任何解决方案。 非常感谢您的帮助。 编辑: 这是我的项目ClickSupport。kt: 这是一个包含整个错误的屏幕截图,以及错误

    • 我有静态函数,它仅限于一些上下文(例如仅用于文档)。有两种替代方法可以将其定义为顶层函数或对象中的函数。 1. 2. 顶级函数的优点是它没有被包装在一个对象中,缺点是它可以从任何地方访问,没有类名前缀。这种情况有优点还是缺点或最佳实践?

    • 在Kotlin中,我覆盖了这两个Google登录功能: 检查与谷歌的连接是否失败。 问题是,有时当我关闭包含用户帐户的对话框时,该对话框会在活动启动时弹出 像这样: 我得到了一个带有以下logcat的<code>IllegalArgumentException</code> E/AndroidRuntime:致命异常:主进程:com.dancam.subscriptions,PID:6346 Ja

    • 根据我所学到的,抽象是一个隐藏内部实现的概念。 在Java中,我们可以通过接口、抽象关键字(类/方法)以及方法来实现抽象。例如扫描器s=新扫描器(System.in);int x=s.nextint();这里我们不需要了解nextInt()方法的任何内容。所以我可以说这是抽象。 同样的事情也可以在C语言中用printf()实现 所以,如果即使在非OOP语言中也能实现抽象,那么为什么在基于OOP的语

    • 我试图从中的访问,将其放入查询中。我到处都在搜索如何访问它,但到目前为止我什么也没找到。我将非常感谢您的帮助:) 这里有一段代码: