使用“by lazy"进行属性初始化与“lateinit"

本文介绍了使用“by lazy"进行属性初始化与“lateinit"的处理方法,对大家解决问题具有一定的参考价值

问题描述

在 Kotlin 中,如果您不想在构造函数内部或类主体顶部初始化类属性,您基本上有这两个选项(来自语言参考):

  1. 延迟初始化

<块引用>

lazy() 是一个函数,它接受一个 lambda 并返回一个 Lazy 的实例,它可以作为实现惰性属性的委托:第一个调用 get() 执行传递给 lazy() 的 lambda 并记住结果,随后对 get() 的调用只返回记住的结果.

示例

公共类你好{val myLazyString:由惰性字符串 {Hello";}}

因此,无论在何处,对 myLazyString 的第一次调用和后续调用都将返回 Hello

  1. 后期初始化

<块引用>

通常,声明为非空类型的属性必须在构造函数中初始化.然而,这通常并不方便.例如,可以通过依赖注入或在单元测试的 setup 方法中初始化属性.在这种情况下,您不能在构造函数中提供非空的初始化程序,但您仍然希望在引用类体内的属性时避免空检查.

要处理这种情况,您可以使用 lateinit 修饰符标记该属性:

public class MyTest {lateinit var 主题:TestSubject@SetUp fun setup() { subject = TestSubject() }@Test fun test() { subject.method() }}

修饰符只能用于在类的主体内(而不是在主构造函数中)声明的 var 属性,并且仅当该属性没有自定义 getter 或 setter 时.属性的类型必须是非空的,并且不能是原始类型.

那么,如何正确选择这两个选项,因为它们都可以解决同样的问题?

解决方案

以下是 lateinit varby lazy { ... } 委托属性的显着区别:

  • lazy { ... } 委托只能用于 val 属性,而 lateinit 只能用于 vars,因为不能编译成final字段,所以不能保证不变性;

  • lateinit var 有一个存储值的后备字段,并且 by lazy { ... } 创建一个委托对象,其中的值存储一次计算,将委托实例的引用存储在类对象中,并为与委托实例一起使用的属性生成 getter.因此,如果您需要类中存在的支持字段,请使用 lateinit;

  • 除了 val 之外,lateinit 不能用于可空属性或 Java 原始类型(这是因为 null用于未初始化的值);

  • lateinit var 可以从任何可以看到对象的地方初始化,例如从框架代码内部,单个类的不同对象可能有多个初始化场景.by lazy { ... } 反过来,定义了属性的唯一初始化器,它只能通过覆盖子类中的属性来更改.如果您希望您的属性以一种可能事先未知的方式从外部初始化,请使用 lateinit.

  • 初始化 by lazy { ... } 默认情况下是线程安全的,并保证初始化器最多被调用一次(但这可以通过使用 另一个lazy 重载).对于 lateinit var,在多线程环境中正确初始化属性取决于用户的代码.

  • Lazy 实例可以保存、传递,甚至可以用于多个属性.相反,lateinit vars 不存储任何额外的运行时状态(只有 null 在未初始化值的字段中).

  • 如果您持有对 Lazy 实例的引用,isInitialized() 允许你检查它是否已经被初始化(你可以从委托属性中通过反射获取此类实例).要检查是否已初始化 lateinit 属性,您可以 从 Kotlin 1.2 开始使用 property::isInitialized.

  • 由lazy { ... } 传递给 的lambda 可以从上下文中捕获引用,并将其用于closure .. 然后它会存储引用并仅在属性初始化后释放它们.这可能会导致对象层次结构(例如 Android 活动)不会被释放太长时间(或者永远不会被释放,如果该属性仍然可以访问并且从未被访问过),因此您应该小心在初始化程序 lambda 中使用的内容.

另外,问题中没有提到另一种方法:Delegates.notNull(),适用于非空属性的延迟初始化,包括Java基本类型的属性.

In Kotlin, if you don't want to initialize a class property inside the constructor or in the top of the class body, you have basically these two options (from the language reference):

  1. Lazy Initialization

lazy() is a function that takes a lambda and returns an instance of Lazy<T> which can serve as a delegate for implementing a lazy property: the first call to get() executes the lambda passed to lazy() and remembers the result, subsequent calls to get() simply return the remembered result.

Example

public class Hello {

   val myLazyString: String by lazy { "Hello" }

}

So, the first call and the subsequential calls, wherever it is, to myLazyString will return Hello

  1. Late Initialization

Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.

To handle this case, you can mark the property with the lateinit modifier:

public class MyTest {
   
   lateinit var subject: TestSubject

   @SetUp fun setup() { subject = TestSubject() }

   @Test fun test() { subject.method() }
}

The modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.

So, how to choose correctly between these two options, since both of them can solve the same problem?

解决方案

Here are the significant differences between lateinit var and by lazy { ... } delegated property:

  • lazy { ... } delegate can only be used for val properties, whereas lateinit can only be applied to vars, because it can't be compiled to a final field, thus no immutability can be guaranteed;

  • lateinit var has a backing field which stores the value, and by lazy { ... } creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, use lateinit;

  • In addition to vals, lateinit cannot be used for nullable properties or Java primitive types (this is because of null used for uninitialized value);

  • lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy { ... }, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit.

  • Initialization by lazy { ... } is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using another lazy overload). In the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments.

  • A Lazy instance can be saved, passed around and even used for multiple properties. On contrary, lateinit vars do not store any additional runtime state (only null in the field for uninitialized value).

  • If you hold a reference to an instance of Lazy, isInitialized() allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can use property::isInitialized since Kotlin 1.2.

  • A lambda passed to by lazy { ... } may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.

Also, there's another way not mentioned in the question: Delegates.notNull(), which is suitable for deferred initialization of non-null properties, including those of Java primitive types.

这篇关于使用“by lazy"进行属性初始化与“lateinit"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2

admin_action_{$_REQUEST[‘action’]}

do_action( "admin_action_{$_REQUEST[‘action’]}" )动作钩子::在发送“Action”请求变量时激发。Action Hook: Fires when an ‘action’ request variable is sent.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$_REQUEST['action']引用从GET或POST请求派生的操作。源码(Source)更新版本源码位置使用被使用2.6.0 wp-admin/admin.php:...

日期:2020-09-02 17:44:16 浏览:1153

admin_footer-{$GLOBALS[‘hook_suffix’]}

do_action( "admin_footer-{$GLOBALS[‘hook_suffix’]}", string $hook_suffix )操作挂钩:在默认页脚脚本之后打印脚本或数据。Action Hook: Print scripts or data after the default footer scripts.目录锚点:#说明#参数#源码说明(Description)钩子名的动态部分,$GLOBALS['hook_suffix']引用当前页的全局钩子后缀。参数(Parameters)参数类...

日期:2020-09-02 17:44:20 浏览:1053

customize_save_{$this->id_data[‘base’]}

do_action( "customize_save_{$this-&gt;id_data[‘base’]}", WP_Customize_Setting $this )动作钩子::在调用WP_Customize_Setting::save()方法时激发。Action Hook: Fires when the WP_Customize_Setting::save() method is called.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_data...

日期:2020-08-15 15:47:24 浏览:794

customize_value_{$this->id_data[‘base’]}

apply_filters( "customize_value_{$this-&gt;id_data[‘base’]}", mixed $default )过滤器::过滤未作为主题模式或选项处理的自定义设置值。Filter Hook: Filter a Customize setting value not handled as a theme_mod or option.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_date['base'],指的是设置...

日期:2020-08-15 15:47:24 浏览:882

get_comment_author_url

过滤钩子:过滤评论作者的URL。Filter Hook: Filters the comment author’s URL.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/comment-template.php:32610...

日期:2020-08-10 23:06:14 浏览:923

network_admin_edit_{$_GET[‘action’]}

do_action( "network_admin_edit_{$_GET[‘action’]}" )操作挂钩:启动请求的处理程序操作。Action Hook: Fires the requested handler action.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$u GET['action']引用请求的操作的名称。源码(Source)更新版本源码位置使用被使用3.1.0 wp-admin/network/edit.php:3600...

日期:2020-08-02 09:56:09 浏览:865

network_sites_updated_message_{$_GET[‘updated’]}

apply_filters( "network_sites_updated_message_{$_GET[‘updated’]}", string $msg )筛选器挂钩:在网络管理中筛选特定的非默认站点更新消息。Filter Hook: Filters a specific, non-default site-updated message in the Network admin.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分$_GET['updated']引用了非默认的...

日期:2020-08-02 09:56:03 浏览:852

pre_wp_is_site_initialized

过滤器::过滤在访问数据库之前是否初始化站点的检查。Filter Hook: Filters the check for whether a site is initialized before the database is accessed.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/ms-site.php:93910...

日期:2020-07-29 10:15:38 浏览:823

WordPress 的SEO 教学:如何在网站中加入关键字(Meta Keywords)与Meta 描述(Meta Description)?

你想在WordPress 中添加关键字和meta 描述吗?关键字和meta 描述使你能够提高网站的SEO。在本文中,我们将向你展示如何在WordPress 中正确添加关键字和meta 描述。为什么要在WordPress 中添加关键字和Meta 描述?关键字和说明让搜寻引擎更了解您的帖子和页面的内容。关键词是人们寻找您发布的内容时,可能会搜索的重要词语或片语。而Meta Description则是对你的页面和文章的简要描述。如果你想要了解更多关于中继标签的资讯,可以参考Google的说明。Meta 关键字和描...

日期:2020-10-03 21:18:25 浏览:1682

谷歌的SEO是什么

SEO (Search Engine Optimization)中文是搜寻引擎最佳化,意思近于「关键字自然排序」、「网站排名优化」。简言之,SEO是以搜索引擎(如Google、Bing)为曝光媒体的行销手法。例如搜寻「wordpress教学」,会看到本站的「WordPress教学:12个课程…」排行Google第一:关键字:wordpress教学、wordpress课程…若搜寻「网站架设」,则会看到另一个网页排名第1:关键字:网站架设、架站…以上两个网页,每月从搜寻引擎导入自然流量,达2万4千:每月「有机搜...

日期:2020-10-30 17:23:57 浏览:1292