Kotlin 知识梳理(4) 数据类、类委托 及 object 关键字

1、本文概要

本文是对<<Kotlin in Action>>的学习笔记,若是须要运行相应的代码能够访问在线环境 try.kotlinlang.org,这部分的思惟导图为: 框架

2、数据类和类委托

2.1 数据类:自动生成通用方法的默认实现

在平时的开发中,咱们每每会使用许多的xxBean对象用做数据容器,而在定义这些对象时,通常会重写它的如下三个方法:函数

  • equals:用来比较实例
  • hashCode:用来做为例如HashMap这种基于哈希容器的类
  • toString:用来为类生成按声明顺序排列的全部字段的字符串表达形式

Kotlin中,只须要为你的类添加data关键字,以上这些必要的方法就会自动生成好,例以下面的例子,咱们演示了以上三个方法的做用: 学习

运行结果为:

equalshashCode方法会将全部在主构造方法中声明的属性归入考虑:设计

  • equals方法会检测全部的属性的值是否相等
  • hashCode方法会返回一个根据全部属性生成的哈希值

数据类和不可变性

在设计数据类时,应当尽可能只使用只读的属性,让数据类的实例不可变,由于若是不这样,被用做键的对象在加入HashMap或者相似容器后被修改了,容器会进入一种无效的状态。3d

为了让使用不可变对象变得容易,Kotlin编译器为它们生成了copy方法,并在copy的同时修改某些属性的值,copy出来的副本有着单独的声明周期并且不会影响代码中引用原始实例的位置,使用方法以下: code

2.2 类委托

当咱们须要向一个类添加一些行为时,通常有两种作法:cdn

  • 继承这个类,在子类中增长方法 这种方法的缺点是:当系统不断演进而且基类的实现被修改或者新方法被添加进去时,你作出的关于类的行为的假设会失效。
  • 使用装饰器模式 本质是建立一个新类,实现与原始类同样的接口并将原来的类的实例做为一个字段保存。与原始类拥有一样行为的方法不用修改,只须要直接转发到原始类的实例。这种方法的缺点是:须要至关多的样板代码。

Kotlin将委托做为一个语言级别的功能作了头等支持。不管何时实现一个接口,你均可以使用by关键字 将接口的实现委托到另外一个对象;当须要修改某些方法的行为时,能够重写它们,这样你的方法就会被调用而不是使用生成的方法,能够保留感到满意的委托给内部的实例中的默认实现。 对象

运行结果为:

3、object 关键字

object关键字在多种状况下出现,它的核心理念为:这个关键字 定义一个类并同时建立一个实例,下面咱们介绍它的三个应用场景:blog

  • 对象声明 是定义单例的一种方式
  • 伴生对象 能够持有工厂方法和其它与这个类相关,但在调用时并不依赖类实例的方法,它们的成员能够经过类名来访问。
  • 对象表达式 用来替代Java的匿名内部类。

3.1 对象声明:建立单例易如反掌

Java中,单例模式一般是使用private构造方法,而且用静态字段来持有这个类仅有的实例。继承

而在Kotlin中,经过使用对象声明功能,将类声明与该类的单一实例声明结合到了一块儿。

  • 对象声明经过object关键字引入,一个对象声明能够很是高效地以一句话来定义一个类和一个该类的变量。
  • 一个对象声明能够包含属性、方法、初始化语句块等的声明,可是 不容许声明构造方法,这是由于对象在定义的时候就已经建立了,不须要在其余地方调用构造方法。
  • 对象声明容许使用对象名加.字符的方式来调用方法和访问属性。

继承自接口的对象声明

对象声明能够继承自类和接口,这一般在你使用的框架须要去实现一个接口,可是你的实现不包含任何状态的时候颇有用。

在类中声明对象

在类中使用对象声明时,这样的对象一样只有一个单一实例:它们在每一个容器类的实例中具备相同的实例。

运行结果为:

在 Java 中使用 Kotlin 对象

若是要在Java中使用Kotlin中的声明对象,能够经过访问静态的INSTANCE字段:

Kotlin 中的对象声明
Java 的调用方式

3.2 伴生对象:工厂方法和静态成员的地盘

Kotlin的类不能拥有静态成员,做为替代,Kotlin依赖包级别函数(在大多数情形下可以替代Java的静态方法)和对象声明(在其余状况下替代Java的静态方法,同时还包括静态字段),在大多数状况下,推荐使用顶层函数,可是顶层函数不能访问类的private变量。

所以,若是你须要写一个 在没有类实例的状况下 调用可是须要 访问类内部的函数,能够将其写成那个类中的 对象声明的成员

在类中定义的对象之一可使用一个特殊的关键字来标记 companion,若是这样作,就得到了直接 经过容器类名称来访问这个对象的方法和属性的能力,再也不须要显示地指明对象的名称,下面是一个基础的示例:

运行的结果为:

使用工厂方法建立对象

下面是使用伴生对象来实现工厂方法的例子:

3.3 做为普通对象使用的伴生对象

伴生对象是一个声明在类中的普通对象,它能够有名字,实现一个接口或者有扩展函数或属性。假设咱们须要在对象和JSON之间进行序列化和反序列化,能够将序列化的逻辑放在伴生对象中。

运行结果为:
在大多数状况下,经过包含伴生对象的类的名字(也就是例子中的 Person类)来引用伴生对象,因此没必要关心它的名字,若是省略了伴生对象的名字,默认的名字将会分配为 Companion

在伴生对象中实现接口

就像其它对象声明同样,伴生对象也能够实现接口,能够将包含它的类的名字当作实现了该接口的对象实例来使用。

运行结果为:

伴生对象扩展

Kotlin 知识梳理(2) - 函数的定义与调用 中,咱们介绍了经过扩展函数,能够经过代码库中其它地方定义类实例调用的方法,可是若是你须要定义能够经过 类自身调用的方法,就像伴生对象方法或者Java静态方法该怎么办呢?

举例来讲,若是类有一个伴生对象,能够经过在其上定义扩展函数来作到这一点,即类C有一个伴生对象,而且在C.Companion上定义了一个扩展函数func,则能够经过C.fun()来调用它。

下面,咱们为Person类的伴生对象定义一个扩展函数:

当调用toJson就像是它是一个伴生对象定义的方法同样,可是实际上它是做为扩展函数在外部定义的。而为了可以为你的类定义扩展,必须在其中声明一个对象,即便是空的。

3.4 对象表达式:改变写法的匿名内部类

object关键字不只可以用来代表单例式的对象,还能用来声明 匿名对象,它替代了Java中匿名内部类的用法。例如,让咱们来看看怎样将一个典型的匿名内部类用法转换成Kotlin

运行结果为:

将匿名对象存储到变量中

除了去掉对象的名字外,语法与对象声明相同的。对象表达式声明了一个类并建立了该类的一个实例,可是没有给这个类或是实例分配一个名字。一般来讲,它们都是不须要名字的,由于你会将这个对象用做一个函数调用的参数。若是你须要给对象分配一个名字,能够将其存储到一个变量中:

在对象表达式中修改变量的值

Java的匿名类同样,在对象表达式中的代码能够访问建立它的函数中的变量,可是与Java不一样,访问并被限制在final变量,还能够在对象表达式中修改变量的值。
运行结果为:

更多文章,欢迎访问个人 Android 知识梳理系列:

相关文章
相关标签/搜索