7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. Kotlin

Kotlin从头开始:高级属性和类

Scroll to top
Read Time: 4 mins
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Classes and Objects
Kotlin From Scratch: Abstract Classes, Interfaces, Inheritance, and Type Alias

Chinese (Simplified) (中文(简体)) translation by Zhang Xiang Liang (you can also view the original English article)

Kotlin  是一种编译为Java字节码的现代编程语言。它是免费的,  开源,并有望使编码为Android更有趣。

在  前面的文章中,您了解了Kotlin类和对象。在本教程中,我们将继续学习更多关于属性的内容,并通过探索以下内容来深入了解Kotlin中的高级类型:

  • 后期初始化的属性
  • 内联属性 
  • 扩展属性
  • 数据,枚举,嵌套和密封类

1.后期初始化属性

我们可以在Kotlin中声明一个非空属性作为后期初始化。这意味着非空属性在声明时不会被初始化,并且值实际的初始化不会通过任何构造函数发生 - 相反,它将通过方法或依赖注入来延迟初始化。

我们来看一个例子来理解这个独特的属性修饰符。

在上面的代码中,我们声明了一个可变的nullable repository属性,它是类型RepositoryPresenter- 在类中- 然后我们在声明过程中将此属性初始化为null。 我们 在Presenter类中有一个initRepository()方法,稍后用实际的Repository实例重新初始化这个属性。请注意,该属性也可以使用像Dagger这样的依赖注入器来赋值。    

现在,我们要调用此repository 属性的方法或属性  ,我们必须执行空检查或使用安全调用操作符。为什么? 因为该repository属性是可空类型(Repository?)。(如果您需要进一步了解Kotlin的可空性,请访问Nullability,Loops和Conditions)

为了避免每次我们需要调用属性方法时进行空检查,我们可以使用lateinit修饰符标记该属性  - 这意味着我们已经声明该属性(这是另一个类的实例)为后期初始化的(意思是属性稍后将被初始化)。  

现在,只要我们等待该属性被赋予一个值,我们就可以安全地访问该属性的方法而不进行任何空的检查。属性初始化可以通过setter方法或通过依赖注入来实现。 

请注意,如果我们在初始化之前尝试访问属性的方法,我们将得到一个  kotlin.UninitializedPropertyAccessException 而不是一个  NullPointerException。在这种情况下,异常消息将是“lateinit属性存储库尚未初始化”。 

当使用以下命令延迟属性初始化时,还要注意以下限制lateinit:

  • 它必须是可变的(用var声明)。
  • 属性类型不能是基本类型,例如,  IntDoubleFloat,等。
  • 该属性不能有自定义的getter或setter。

2.  内联属性

在  高级函数中,我引入了inline高阶函数的修饰符 - 这有助于优化任何接受lambda作为参数的高阶函数。

在Kotlin中,我们也可以inline在属性上使用这个修饰符。使用此修饰符将优化对该属性的访问。

我们来看一个实际的例子。

在上面的代码中,我们有一个普通的属性,  nickName,没有  inline修饰符。如果我们反编译代码片段,使用Show Kotlin Bytecode功能(如果您使用的是IntelliJ IDEA或Android Studio,请使用  Tools  > Kotlin > Show Kotlin Bytecode),我们将看到以下Java代码:

在上面生成的Java代码中(为了简洁,删除了生成代码的某些元素),您可以看到在该main()方法内部,编译器创建了一个Student名为getNickName() 方法的对象,然后打印其返回值。  

现在我们来指定该属性  inline ,并比较生成的字节码。

我们只需在变量varval修饰符之前插入inline  修饰符:  。 以下是为这个内联属性生成的字节码:

再次删除了一些代码,但要注意的关键是该main()方法。编译器复制属性get()函数体,并将其粘贴到调用点(这种机制类似于内联函数)。 

我们的代码已经过优化,因为不需要创建对象并调用属性getter方法。但是,正如在内联函数中所讨论的那样,我们会有比以前更大的字节码,所以谨慎使用。 

还要注意,这种机制可以用于没有后台字段的属性(记住,当你想修改或使用字段数据时,后台字段只是属性使用的字段)。 

3.  扩展属性 

在  高级函数中,  我还讨论了扩展函数 - 这些扩展函数使我们能够扩展具有新功能的类,而不必从该类继承。Kotlin还为属性提供了一种类似的机制,称为  扩展属性。 

在  高级函数后,  我们 用接收器String类型定义了一个扩展函数uppercaseFirstLetter() 。 在这里,我们已将其转换为顶级扩展属性。请注意,您必须在您的属性上定义一个getter方法才能使其工作。 

因此,借助关于扩展属性的新知识,您将知道如果您希望某个类应该有一个不可用的属性,则可以自由创建该类的扩展属性。 

4.  数据类

让我们从一个典型的Java类或POJO(Plain Old Java Object)开始。 

正如你所看到的,我们需要明确的代码的类属性访问器:getter和setter,以及  hashcode,  equalstoString 方法(虽然IntelliJ IDEA的,Android的工作室,或AutoValue库可以帮助我们来生成)。  我们在一个典型的Java项目的数据层中主要看到这种样板代码。(为了简洁起见,我删除了字段访问器和构造器)。

很酷的是,Kotlin团队为我们提供了data修改器,以避免编写这些样板文件。

现在让我们在Kotlin中写上前面的代码。

真棒!我们只是data 在class 关键字之前指定  修饰符  来创建数据类,就像我们在BlogPost 上面的Kotlin类中所做的一样  。 现在  equals,  hashcode,  toString,  copy,和多组分方法将引擎盖为我们下创建的。请注意,数据类可以扩展其他类(这是Kotlin 1.1的新功能)。

equals 方法

该方法比较两个对象是否相等,如果相等则返回true,否则返回false。换句话说,它比较两个类实例是否包含相同的数据。 

在Kotlin中,使用相等运算符==将equals 在幕后调用该  方法。

hashCode 方法 

此方法返回在用于快速存储,并存储在基于散列的集合数据结构的数据的检索,例如一个整数值  HashMap 和  HashSet 集合类型。

toString 方法

这个方法返回一个String 对象的  表示。

通过调用类实例,我们得到一个返回给我们的字符串对象 - Kotlin为我们调用toString() 对象  。但是如果我们不把data 关键字放进去  ,看看我们的对象字符串表示是什么: 

更少的信息!

copy 方法

这个方法允许我们创建一个具有所有相同属性值的对象的新实例。换句话说,它创建了对象的副本。 

Kotlin中关于copy方法的一件很酷的事情就是在复制期间改变属性的能力。 

如果您是Java编码器,则此方法与clone() 您已熟悉的方法类似  。但是Kotlin  copy 方法具有更强大的功能。 

破坏性声明

在  Person 类中,由于data 关键字放置在类中,因此编译器会自动为我们生成两个方法  。这两个方法以“component”为前缀,后跟一个数字后缀:  component1(),  component2()。 每种方法都表示该类型的各个属性。请注意,后缀对应于在主构造函数中声明的属性的顺序。

因此,在我们的示例中,调用  component1() 将返回名字,并且调用  component2() 将返回最后的名字。

尽管使用这种风格调用属性很难理解和阅读,所以明确调用属性名称要好得多。但是,这些隐式创建的属性确实有一个非常有用的用途:它们让我们执行一个解构声明,其中我们可以将每个组件分配给一个局部变量。

我们在这里所做的是直接将Person类型的第一个和第二个属性(firstName 和  lastName)   分别赋值给变量firstName 和  变量  lastName。我还在Packages和Basic Functions  的最后一部分  讨论了称为解构声明的机制   。 

5.  嵌套类

在 文章 更有趣的函数,我告诉你,Kotlin支持本地函数或嵌套函数 - 这是在另一个函数内声明的函数。那么,Kotlin也同样支持嵌套类 - 在另一个类中创建的类。

我们甚至可以调用嵌套类的公共函数,如下所示 - Kotlin中的static 嵌套类相当于Java中的  嵌套类。请注意,嵌套类不能存储对其外部类的引用。 

我们也可以自由地将嵌套类设置为私有 - 这意味着我们只能NestedClass 在该范围内  创建一个实例  OuterClass。 

内部类

内部类,在另一方面,可以参考它在声明的外部类。要创建一个内部类,我们把该  inner 关键字前  class 在嵌套类的关键字。

在这里,我们引用  OuterClass 从  InnerClass 使用   this@OuterClass。

6.  枚举类

枚举类型声明由标识符表示的一组常量。这种特殊的类是由关键字enum之前指定的class关键字创建的。 

要根据名称检索枚举值(就像在Java中一样),我们这样做:

或者我们可以使用Kotlin enumValueOf()辅助方法以通用方式访问常量:

另外,我们可以像这样获得所有的值(例如Java枚举类型):

最后,我们可以使用Kotlin enumValues()辅助方法以通用方式获取所有枚举条目:

这将返回一个包含枚举条目的数组。  

枚举构造函数

就像一个普通的类一样,enum类型可以有自己的构造函数,其属性与每个枚举常量相关联。

在Country枚举类型的主构造函数中,我们callingCodes为每个枚举常量定义了不可变属性。在每个常量中,我们向构造函数传递了一个参数。 

然后我们可以像这样访问常量属性:

7.密封类

Kotlin中的密封类是一个抽象类(你从来不打算从它创建对象),其他类可以扩展。这些子类是在密封的类体中定义的 - 在同一个文件中。因为所有这些子类都是在密封的类体内定义的,所以我们可以通过查看文件来了解所有可能的子类 因为所有这些子类都是在密封的类体内定义的,所以我们可以通过查看文件来了解所有可能的子类。 

我们来看一个实际的例子。

为了将类声明为密封类,我们在类声明头中的sealed修饰符之前插入修饰符class- 在我们的例子中,我们将该Shape类声明为sealed。一个密封的类没有它的子类是不完整的 - 就像一个典型的抽象类 - 所以我们必须在同一个文件中声明各个子类(在这种情况下是shape.kt)。请注意,您不能从另一个文件中定义密封类的子类。  请注意,您不能从另一个文件中定义密封类的子类。 

在上面的代码中,我们指定的Shape类只能通过类来扩展  Circle,  Triangle和  Rectangle

Kotlin的密封课程有以下附加规则:

  • 我们可以将修饰符添加  abstract 到密封类,但这是多余的,因为密封类默认是抽象的。
  • 密封的类不能有  open或final修饰符。 
  • 我们也可以自由地将数据类和对象声明为一个密封类的子类(它们仍然需要在同一个文件中声明)。 
  • 密封类不允许使用公共构造函数 - 它们的构造函数默认是私有的。 

扩展密封类的子类的类可以放在同一个文件或其他文件中。密封的类子类必须使用open 修饰符标记  (在下一篇文章中,您将在Kotlin中学习更多关于继承的知识)。 

一个密封的类和它的子类在when表达式中非常方便。例如:

这里编译器很聪明,可以确保我们涵盖了所有可能的  when情况。这意味着没有必要添加该else 条款。 

如果我们要这样做:

代码不会编译,因为我们没有包含所有可能的情况。我们会遇到以下错误:

因此,我们可以包括is Rectangle案例或包含else条款来完成when表达式。

结论

在本教程中,您了解了更多关于Kotlin的课程。我们介绍了关于类属性的以下内容:

  • 后期初始化
  • 内联属性 
  • 扩展属性

此外,您还了解了一些很酷和高级的类,例如数据,枚举,嵌套和密封类。在Kotlin From Scratch系列的下一个教程中,您将介绍Kotlin中的接口和继承。再见!

要了解有关Kotlin语言的更多信息,我建议访问  Kotlin文档。或者在Envato Tuts上查看我们的其他一些Android应用程序开发帖子!

关注我们的公众号
Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.