Kotlin:在构造函数中初始化类属性
Posted
技术标签:
【中文标题】Kotlin:在构造函数中初始化类属性【英文标题】:Kotlin: Initialize class attribute in constructor 【发布时间】:2015-09-11 15:22:02 【问题描述】:我创建了一个带有类属性的 Kotlin 类,我想在构造函数中对其进行初始化:
public class TestClass
private var context : Context? = null // Nullable attribute
public constructor(context : Context)
this.context = context
public fun doSomeVoodoo()
val text : String = context!!.getString(R.string.abc_action_bar_home_description)
不幸的是,我必须使用“?”将属性声明为 Nullable。符号,尽管属性将在构造函数中初始化。将此属性声明为 Nullable-attribute 使得始终需要使用“!!”强制 NonNull-value或使用“?”提供 Null 检查。
如果类属性将在构造函数中初始化,有什么方法可以避免这种情况?我想感谢这样的解决方案:
public class TestClass
private var context : Context // Non-Nullable attribute
public constructor(context : Context)
this.context = context
public fun doSomeVoodoo()
val text : String = context.getString(R.string.abc_action_bar_home_description)
【问题讨论】:
第二个例子是使用 Kotlin 0.12.213。您使用的是什么 Kotlin 版本? 它有效。我已经使用了 0.12.613。但我想,我做错了什么。对不起! 有更多案例可用,我添加了完整覆盖的答案。 【参考方案1】:如果您在构造函数中所做的唯一事情是赋值, 那么您可以将主构造函数与私有属性一起使用。
例如:
public class TestClass(private val context: Context)
public fun doSomeVoodoo()
val text = context.getString(R.string.abc_...)
【讨论】:
@DamonYuan 你有什么建议? @D3xter 我认为这很容易导致内存泄漏。【参考方案2】:我有一个类似的问题,我不想在构建后抓住对象。使用lazy
或lateinit
导致字节码效率低下,因此经过一些研究后,我决定采用这种方法并返回发布答案以防万一:
解决方案
class TestClass(context: Context)
private val homeDescription: String
init
homeDescription = context.getString(R.string.abc_action_bar_home_description)
fun doSomeVoodoo()
val text : String = homeDescription
或者,以上可以进一步简化为:
class TestClass(context: Context)
private val homeDescription: String = context.getString(R.string.abc_action_bar_home_description)
fun doSomeVoodoo()
val text : String = homeDescription
反编译字节码
而且反编译的 java 版本感觉比其他方法更容易接受,并且在构造后没有对上下文的引用:
public final class TestClass
private final String homeDescription;
public final void doSomeVoodoo()
String text = this.homeDescription;
public TestClass(@NotNull Context context)
Intrinsics.checkParameterIsNotNull(context, "context");
super();
String var10001 = context.getString(2131296256);
Intrinsics.checkExpressionValueIsNotNull(var10001, "context.getString(R.stri…ion_bar_home_description)");
this.homeDescription = var10001;
【讨论】:
【参考方案3】:如 D3xter 所示,您可以选择在构造函数中设置它。您还有其他选择。他们都在这里……
在构造函数中创建属性(根据@D3xter),这是由主构造函数直接初始化的简单属性最常见的情况:
class TestClass(private val context: Context)
fun doSomeVoodoo()
val text : String = context.getString()
您可以声明 val
属性而不对其进行初始化,假设所有可能的构造函数都确实对其进行了初始化(根据您提出的问题中的第二个示例)。 当您有多个可以不同方式初始化值的构造函数时,这是正常的:
public class TestClass
private val context: Context
public constructor(context : Context)
this.context = context
// alternative constructor
public constructor(pre: PreContext)
this.context = pre.readContext()
public fun doSomeVoodoo()
val text : String = context.getString()
您可以传入不是属性声明的构造函数参数,然后在属性初始化中使用这些参数。 当您有更复杂的初始化或需要使用委托属性时,这很常见:
class TestClass(context: PreContext)
private val context : Context by lazy context.readContext()
private val other: List<Items> = run
context.items.map it.tag .filterNotNull()
private val simpleThing = context.getSimple()
fun doSomeVoodoo()
val text : String = context.getString()
当您在构造过程中无法初始化值时使用lateinit
modifier,但您确定它会在您第一次读取访问之前完成。 当依赖注入、IoC 容器或其他东西创建你的类的空版本然后立即初始化它时,这很常见:
class TestClass()
private lateinit var context : Context // set by something else after construction
fun doSomeVoodoo()
val text : String = context.getString()
对于lateinit
,该属性当前必须是var
,并且不适用于原始类型。
如果您使用专为此目的设计的委托,例如Delegates.notNull()
,您也可以声明var
属性而不初始化它。 这类似于lateinit
,当您想要一个没有初始状态的var
时很常见,但在构建后在未知时间点设置后:
public class TestClass()
private var context: Context by Delegates.notNull()
public fun doSomeVoodoo()
// if context is not set before this is called, an exception is thrown
val text : String = context.getString()
【讨论】:
以上是关于Kotlin:在构造函数中初始化类属性的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin类的初始化 ③ ( init 初始化块 | 初始化顺序 : 主构造函数属性赋值 -> 类属性赋值 -> init 初始化块代码 -> 次构造函数代码 )
Kotlin类的初始化 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )
Kotlin类与对象 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )