扩展

Kotlin能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。这通过叫做扩展的特殊声明完成。

例如,可以为一个不能修改的、来自三方的库中的类编写一个新的函数。这个新增的函数就像那个原始本来就有的函数一样,可以用普通的方法调用。这种机制成为扩展函数。此外,也有扩展属性,允许为一个已经存在的类添加新的属性

扩展函数

声明一个扩展函数,需要用一个接收者类型也就是被扩展的类型来作为它的前缀。

下面代码为 MutableList<Int> 添加一个 swap 函数:

fun MutableList<Int>.swap(index1: Int, index2: Int){val tmp = this[index1]  //"this"对应列表this[index1] = this[index2]this[index2] = tmp
}//这个 this 关键字在扩展函数内部对应到接收者对象(传过来的在点符号之前的对象)
//调用如下
val list = mutableListOf(1,2,3)
//swap()内部中的 this 会保存 list 的值
list.swap(0,2)//当然我们可以泛化这个函数
//为了在接收者类型表达式中使用泛型,我们要在函数名前声明泛型参数
fun <T> MutableList<T>.swap(index1: Int, index2: Int){val tmp = this[index1]this[index1] = this[index2]this[index2] = tmp
}

扩展是静态解析的:扩展并不能真正修改他们所扩展的类。通过定义一个扩展,并没有在一个类中插入新成员,仅仅是可以通过该类型的变量用点表达式去调用这个新函数。想强调的是扩展函数是静态分发的,即他们不是根据接收者类型的虚方法,这意味着调用的扩展函数是由函数调用所在的表达式的类型来决定的,而不是由表达式运行时求值结果决定的

如果一个类定义有一个成员函数与一个扩展函数,而这两个函数又有相同的行参类型、相同的名字,并且都适用于给定的参数,这种情况总是取成员函数

可空接收者:注意可以为可空的接受者类型定义扩展。这样的扩展可以在对象变量上调用,即使其值为null,并且可以在函数内检测 this==null,这能让在没有检测null的时候调用Kotlin中的toString():检测发生在扩展函数的内部

fun Any?.toString(): String{if(this == null) return "null"//空检测之后,"this"会自动转换为非空类型,所以下面的 toString()解析为Any类的成员函数return toString()
}

扩展属性

与函数类似,由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。所以扩展属性不能有初始化器,他们的行为只能由显示提供的 getters/setters 定义

伴生对象的扩展

如果一个类定义有一个伴生对象,也可以为伴生对象定义扩展函数与属性。就像伴生对象的常规成员一样,可以只使用类名作为限定符来调用伴生对象的扩展成员

class MyClass{companion object{}  //伴生对象,将被称为 “Companion”
}fun MyClass.Companion.printCompanion(){ println("companion")}

扩展的作用域

大多数时候我们在顶层定义扩展——直接在包里

要使用定义包之外的一个扩展,我们需要在调用方导入它

扩展声明为成员

在一个类内部你可以为另一个类声明扩展。在这样的扩展内部,有多个隐式接受者——其中的对象成员可以无需通过限定访问符访问。扩展声明所在的类的实例称为分发接收者,扩展方法调用所在的接收者类型的实例称为扩展接收者

数据类

我们经常创建一些只保存数据的类,在这些类中,一些标准函数往往是从数据机械推导而来的。在Kotlin中,这叫做数据类并标记为 data:

data class User(val name: String, val age: Int)

为了确保生成的代码一致性以及有意义的行为,数据类必须满足以下要求:

  • 主构造函数需要至少一个参数
  • 主构造函数所有的参数需要标记为 val 或 var
  • 数据类不能是抽象、开放、密封或者内部的
  • (在1.1之前)数据类只能实现接口

此外,成员生成遵循关于成员继承的这些规则

  • 如果在数据类体中有显式实现 equals()、hashCode()或者toString(),或者这些函数在父类中有final实现,那么不会生成这些函数,而会使用现有函数
  • 如果父类型就有 open 的 componentN() 函数并且返回兼容的类型,那么会为数据类生成相应的函数,并覆盖父类的实现。如果父类型的这些函数由于签名不兼容或者是final而导致无法覆盖,那么会报错
  • 从一个已具 copy(......) 函数且签名匹配的类型派生一个数据类在Kotlin1.2中已弃用,并且在Kotlin1.3中已禁用
  • 不允许为 componentN() 以及 copy() 函数提供显式实现

在 JVM 中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值

data class User(val name: String = "", val age: Int = 0)

复制

很多情况下,我们需要复制一个对象改变它的一些属性,但是其余部分保持不变,copy() 函数就是为此而生成的

//在User类中,增加copy方法
fun copy(name: String = this.name, age: Int =this.age) = User(name, age) val jack = User(name = "jack", age = 1)
val oldJack = jack.copy(age = 2)

数据类与解构声明

为数据类生成的 Component 函数使它们可在解构声明中使用

val cindy = User("Cindy", 26)
val (name, age) = cindy
//输出 "cindy, 26 years of age"
println("$name, $age years of age")

密封类

密封类用来表示受限的类继承结构:当一个值为有限几种的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例

要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr): Expr()
object NotANumber: Expr()

一个密封类是自身抽象的,它不能直接实例化并可以有抽象成员

密封类不能有非 private 构造函数(其构造函数默认为 private)

使用密封类的好处在于使用 when 表达式的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句,当然只有当使用when作为表达式(使用结果)而不是作为语句时才有用

fun eval(expr: Expr): Double = when(expr){is Const -> expr.numberis Sum   -> eval(expr.e1) + eval(expr.e2)NotANumber -> Double.NaN//不需要else子句因为已经覆盖了所有的情况
}

Kotlin学习——类与对象(二)相关推荐

  1. 三、kotlin的类和对象(二)

    theme: channing-cyan 继承★ kotlin 沿用了 java 的单继承系统, 不允许 c++ 的多继承出现, 但允许 kotlin 接口的多实现 open class Base(v ...

  2. java学习--类与对象

    文章目录 java学习--类与对象 类的定义与对象的使用 类的私有成员的使用 java学习–类与对象 类的定义与对象的使用 类的定义与对象的使用 类的定义步骤: public 类名: //定义成员变量 ...

  3. python代码学习——类与对象提升(继承、超继承,类的例题,魔术方法)

    python代码学习--类与对象提升 继承 继承的特殊属性: 代码示例 方法的重写和覆盖(overrrid) 总结 超继承 继承中的初始化 多继承 例题 类的魔术方法 哈希(hash)和eq方法 bo ...

  4. c++学习13 类与对象(二)c++对象模型和this指针和友元

    类和对象 c++对象模型和this指针 成员变量和成员函数分开存储 在c++中类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象 #include<iostream> us ...

  5. java学习---类与对象

    java学习心得3 文章目录 java学习心得3 类 对象 关键字this 总结与反思 类 一 定义一个Java的类 属性 : 将数据存储在变量中 1成员变量 使用成员变量:对象名.变量名 使用成员方 ...

  6. C++入门学习 类与对象

    类与对象 普通大一新生,C++入门.老师上课不讲课只看视频讲解,效率低.临近期末总结巩固已经学习过的内容. 一.构造函数 定义 类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数-> ...

  7. Python学习——类与对象

    一.编程的两大思想 (1)面向过程 事物比较简单,用简单的线性思维即可解决 (2)面向对象 事物比较复杂,用简单的线性思维无法解决 (3)两者之间的关系 在面对复杂的问题时,宏观上是使用面向对象,但具 ...

  8. Java学习——类和对象(上)

    目录 一.类和对象 1.类的特点 2.类和对象的关系 3.总结 二.类的定义 1.类的定义步骤 2.代码示例 三.对象的使用 1.创建对象 2.使用对象 3.单个对象(代码) 4.多个对象(代码) 四 ...

  9. Java学习 --- 类与对象

    目录 一.问题需求 二.类与对象关系图 三.类与对象的关系 四.什么是类 五.什么是对象 六.对象的内存分布图 一.问题需求 有两只猫,第一只叫小白,年龄:1岁,颜色:白色,第二只叫小黑,年龄:2岁, ...

最新文章

  1. Win32汇编数据对齐相关的伪指令(ALIGN、EVEN、ORG)
  2. margin的塌陷现象
  3. 安全测试的目的,发现哪些问题
  4. [云炬python3玩转机器学习笔记] 3-1 Jupyter Notebook
  5. Spring Boot WebMagic 入库时 mapper注入提示空指针,以及正确的操作
  6. 入门科普:一文看懂NLP和中文分词算法(附代码举例)
  7. 网卡流量统计实用工具nicstat
  8. .NET C# Socket产品性能测试、性能对比报告(包含SuperSocket、HPSocket.Net、TouchSocket)
  9. Windows10 修改键位映射
  10. 使用WSS的Lists.UpdateListItems()方法之被截断的CAML
  11. 小龙秋招【面试笔记】正式发布,速来围观!(已有40+同学斩获大厂offer)
  12. Virtex6 PCIe 超简版基础概念学习(二)(转载)
  13. 【math】Hiden Markov Model 隐马尔可夫模型了解
  14. SAN存储的局限性相关介绍
  15. 【Unity3D游戏开发学习笔记】(六)上帝之手—GameObject的操作
  16. 基于YOLOV3的通用物体检测项目实战---(5)利用DarkNet框架进行YOLOV3模型训练实操(笔记)
  17. 管理员禁止运行此应用的解决办法
  18. 转:前端 100 问:能搞懂80%的请把简历给我
  19. Linux 计算机网络 从 ping 来初窥计算机网络
  20. npx:调用项目内部安装的模块

热门文章

  1. matlab曲线拟合工具箱cftool,Matlab曲线拟合工具箱CFtool使用
  2. 苹果手机微信如何应用双开多开?教程来了
  3. 沙雕程序员在无聊的时候,都搞出了哪些好玩的小玩意...
  4. 如何使用 SQL CREATE TABLE 创建新表
  5. 信息与信息技术(概述+习题)
  6. 偶像眼中的偶像-迈克尔 乔丹
  7. MouseWithoutBorders 无界鼠标
  8. Revit中窗族的立面出图设置和构件显隐
  9. 2020年四川达州中考作文题目及点评
  10. 平衡车设计---------PD