【问题标题】:Understanding Kotlin constructors and supertype initialization了解 Kotlin 构造函数和超类型初始化
【发布时间】:2020-09-15 01:13:12
【问题描述】:

我有这门课:

class MyClass: Fragment(), AdapterView.OnItemSelectedListener {
    companion object {
        fun newInstance(parent: Activity) = MyClass(parent)
    }

    constructor(parent: Activity) {
        this.parent = parent;
    }

在 Fragment() 上给出错误:

没有主构造函数就不可能初始化超类型

然后我跟着Supertype initialization is impossible without primary constructor改成了

class MyClass constructor(): Fragment(), AdapterView.OnItemSelectedListener {
    companion object {
        fun newInstance(parent: Activity) = MyClass(parent)
    }

    constructor(parent: Activity) {
        this.parent = parent;
    }

然后我得到了

这个想法让我改变了

    constructor(parent: Activity) : this() {
        this.parent = parent;
    }

constructorclass MyClass constructor(): Fragment() 上的含义是什么?为什么我必须输入this()

【问题讨论】:

    标签: android kotlin


    【解决方案1】:

    来自the documentation

    Kotlin 中的类可以有一个主构造函数和一个或多个辅助构造函数。主构造函数是类头的一部分:它位于类名(和可选类型参数)之后。

    这是主构造函数的示例:class MyClass constructor()

    仅当您想指定可见性修饰符 (default is public) 和/或注释时,关键字 constructor 才是强制性的,因此以下所有替代方案都是等效的:

    class MyClass public constructor()
    class MyClass constructor()
    class MyClass()
    class MyClass // parenthesis can be omitted if no argument is provided
    

    显然,如果这是您想要的构造函数,则更简洁的版本优于更冗长的版本。

    除了主构造函数之外,您还可以有零个、一个或多个辅助构造函数。辅助构造函数必须直接或间接委托给主构造函数 - 通过调用另一个辅助构造函数 - 示例:constructor(...) : this(...)

    如果您的类没有指定任何主构造函数,则任何辅助构造函数都必须委托给超类。示例:例如constructor(...) : super(...).

    如果您不指定任何主构造函数或辅助构造函数,那么您的类将有一个不带参数的隐式默认公共构造函数(如在 Java 中)。

    所以,回答你的问题:

    constructorclass MyClass constructor(): Fragment() 上的含义是什么?

    它声明了主构造函数,但鉴于您没有指定任何可见性修饰符,也没有任何注释,它可以被省略。这同样适用于括号,因此它可以重写为class MyClass : Fragment()(注意: Fragment() 位声明您的类扩展Fragment 并指定MyClass 的默认构造函数应调用其父级的空构造函数(更多细节@ 987654323@)

    为什么我必须输入this()? 如上所述,这对于辅助构造函数是必需的。


     编辑

    正如我在下面的评论中所解释的,在代码的第一个 sn-p 中,您是:

    • 定义辅助构造函数(constructor(parent: Activity)
    • 未定义主构造函数。那是因为你有一个辅助构造函数,但在你的类定义之后没有括号(即class MyClass()class MyClass constructor() - 它们是等价的,如上所述),所以 Kotlin 不会为你生成默认的空主构造函数
    • 您的类正在扩展Fragment,并且您正在调用父类的构造函数作为类签名的一部分。这需要您的类的主要构造函数,如您所面临的错误突出显示:Supertype initialization is impossible without primary constructor

     如何解决问题?

    一种可能的方式是您所做的,即同时定义主构造函数和辅助构造函数。但是,您可以通过 2 种方式创建 MyClass 的实例:传递或不传递父 Activity。这大致相当于下面的 Java 代码:

    class MyClass extends Fragment {
        private Activity parent;
    
        public MyClass() {
            super()
        }
    
        public MyClass(Activity parent) {
            super()
            this.parent = parent;
        }
    }
    

    您可能不想在没有父 Activity 的情况下构建该类。如果是这种情况,只需摆脱辅助构造函数——你不需要它——并创建一个接受任何你需要的参数的主构造函数。 示例:class MyClass(val parent: Activity) : Fragment()。这是定义主构造函数的一种非常简洁的方法,该构造函数接受输入中的 Activity 并将该参数分配给名为 parent 的实例变量; val 意味着 parent 将是只读的(即 Kotlin 将生成 getter 但不生成 setter),但如果您需要实例变量是可变的,则可以使用 var 代替(即 Kotlin 将生成 getter 和二传手)。

    这大致相当于下面的Java代码:

    class MyClass extends Fragment {
        private Activity parent;
    
        public MyClass(Activity parent) {
            this.parent = parent;
        }
    }
    

    【讨论】:

    • class NameOfClass 之后添加constructor() 有什么意义?我试图再次在类中添加构造函数,但也定义了它,但我知道它已经被声明到类名中
    • 基本上我们原来的 sn-p 中的问题是你没有定义任何主构造函数(类名后面没有括号),你定义了一个辅助构造函数(在输入中接受 Activity 的那个) )。但是,当从类签名(例如class Xxx : Yyy())调用父级的构造函数时,您必须定义一个主构造函数。尝试从您的类中删除辅助构造函数并将您的类定义替换为class MyClass(val parent: Activity): Fragment(), AdapterView.OnItemSelectedListener,它的行为与您预期的一样
    • @GuerlandoOCs 我已经编辑了我的答案,希望它能消除你的疑虑
    猜你喜欢
    • 2019-10-07
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多