内容:
- 数据类
- 密封类
- 嵌套类、内部类
- 枚举类
- 内联类
一、数据类
1、定义
- 只保存数据的类,标记为
data
在这些类中,一些标准函数往往是从数据机械推导而来的。1
data class User(val name: String, val age: Int)
1.1、要求:
- 主构造函数 需要至少有一个参数;
- 主构造函数 的所有参数需要标记为 val 或 var;
- 数据类不能是抽象(
abstract
)、开放(open
)、密封(sealed
)或者内部(inner
)的; - 数据类只能实现接口(
interface
)(在1.1之前)
1.2、导出成员:
编译器自动从主构造函数中声明的所有属性导出以下成员:
equals()
/hashCode()
对;toString()
格式是"User(name=John, age=42)"
;componentN()
函数 按声明顺序对应于所有属性;(数据类【只针对主构造函数内的属性】自动声明componentN()
函数)copy()
函数(见下文)。
1.3、成员生成遵循规则:
如果在数据类体中有显式实现
equals()
、hashCode()
或者toString()
,或者这些函数在父类中有final
实现,那么不会生成这些函数,而会使用现有函数;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24data class Person(var name: String, var age: Int) {
var sex: Int = 0;
override fun toString(): String {
return "name=$name, age=$age, sex=$sex"
}
}
data class Student(var name: String, var age: Int) {
var sex: Int = 0;
}
fun foo() {
var p = Person("zs", 23);
p.sex = 1;
println(p) //name=zs, age=23, sex=1
var s = Student("ls", 21)
s.sex = 3;
println(s) //Student(name=ls, age=21)
}
fun main() {
foo()
}如果超类型具有
open
的componentN()
函数并且返回兼容的类型, 那么会为数据类生成相应的函数,并覆盖超类的实现。如果超类型的这些函数由于签名不兼容或者是final
而导致无法覆盖,那么会报错;从一个已具
copy(……)
函数且签名匹配的类型派生一个数据类在 Kotlin 1.2 中已弃用,并且在 Kotlin 1.3 中已禁用。不允许为
componentN()
以及copy()
函数提供显式实现。
Tip:
在 JVM 中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。
1 | data class User(val name: String = "", val age: Int = 0) |
2、属性
2.1、类体中声明属性
对于那些自动生成的函数(toString
等),编译器 只使用在主构造函数内部定义的属性。如需在生成的实现中排出一个属性,请将其声明在类体中:
1 | data class Person(val name: String) { |
上述数据类中,在 toString()、 equals()、 hashCode() 以及 copy() 的实现中只会用到 `name 属性,并且只有一个 component 函数 component1()。虽然两个 Person 对象可以有不同的年龄,但它们会视为相等。
1 | fun main() { |
2.2、复制 copy
copy
可以复制一个对象改变它的一些属性,但其余部分保持不变。1
2
3data class User(val name: String, val age: Int)
//用法
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)示例:
1
2
3
4
5
6
7
8
9
10
11
12fun main(agrs: Array<String>) {
val u1 = User("zs", 23)
println("u1 : $u1")
val u2 = u1.copy()
println("u2 拷贝u1为:: $u2")
val u3 = u1.copy(name = "ls")
println("u3 拷贝 u1 为 : $u3")
}
// u1 : User(name=zs, age=23)
// u2 拷贝u1为:: User(name=zs, age=23)
// u3 拷贝 u1 为 : User(name=ls, age=23)
2.3、数据类与解构声明
- 解构声明:把一个对象 解构 成很多变量,并且可单独使用每个变量
1
2
3
4
5
6
7
8data class Person(var name: String, var age: Int)
//解构
val (name, age) = person
//示例
val jane = Person("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // 输出 "Jane, 35 years of age"
一个解构声明会被编译成以下代码:
1 | val name = person.component1() |
其中的 component1() 和 component2() 函数是在 Kotlin 中广泛使用的 约定原则 的另一个例子。
二、密封类
1、定义
修饰符:
sealed
密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。
在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
密封类的构造函数在默认情况下是私有的,它也不能允许声明为非私有。
密封类的子类都必须在与密封类自身相同的文件中声明。
1
2
3
4sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()一个密封类自身是抽象的,它不能直接实例化,并可以有抽象方法
1
2
3
4
5sealed class MyClass
fun main(args: Array<String>)
{
var myClass = MyClass() // 编译器错误,密封类型无法实例化。
}扩展密封类子类的类(间接继承者)可以放在任何位置,而无需在同一个文件中。即继承 密封类的子类 的类,可以在其他文件中。
1
2
3
4
5
6sealed class Expr
//和Expr在一个文件中
data class Const(val number: Double) : Expr()
//可以放在其他文件中
data class Test(val number: Double, val name: Int) : Const(number)密封类通常与
when
表达式一起使用
由于密封类的子类将自身类型作为一种情况。 因此,密封类中的when表达式涵盖所有情况,从而避免使用else子句。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
class Rectangle(var length: Int, var breadth: Int): Shape()
// object NotAShape : Shape()
}
fun eval(e: Shape) =
when (e) {
is Shape.Circle ->println("Circle area is ${3.14*e.radius*e.radius}")
is Shape.Square ->println("Square area is ${e.length*e.length}")
is Shape.Rectangle ->println("Rectagle area is ${e.length*e.breadth}")
//else -> "else case is not require as all case is covered above"
// Shape.NotAShape ->Double.NaN
}
fun main(args: Array<String>) {
var circle = Shape.Circle(5.0f)
var square = Shape.Square(5)
var rectangle = Shape.Rectangle(4,5)
eval(circle) //Circle area is 78.5
eval(square) //Square area is 25
eval(rectangle) //Rectagle area is 20
}
三、嵌套类、内部类
1、嵌套类
1 | class Outer { |
2、内部类
- 修饰符:
inner
- 能够访问外部类的成员
1
2
3
4
5
6
7
8class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
3、匿名内部类
1 | window.addMouseListener(object : MouseAdapter() { |
对于 JVM 平台, 如果对象是函数式 Java 接口(即具有单个抽象方法的 Java 接口)的实例, 你可以使用带接口类型前缀的lambda表达式创建它:
1 | var run = Runnable { println("run-----") } |
4、伴生对象
1、声明:
类内部的对象声明可以用companion
关键字标记1
2
3
4
5
6
7
8class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
//该伴生对象(Factory)的成员(create函数)可通过只使用类名作为限定符来调用:
val instance = MyClass.create()2、省略名称:
可以省略伴生对象(如Factory)的名称,在这种情况下将使用名称Companion
:1
2
3
4
5class MyClass {
companion object { }
}
val x = MyClass.Companion
其自身所用的类的名称(不是另一个名称的限定符)可用作对该类的伴生对象 (无论是否命名)的引用:
1 | class MyClass1 { |
注意:
请注意,即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员
实现接口:
1 | interface Factory<T> { |
四、枚举类
- 修饰符:
enum
- 枚举类的最基本的用法是实现类型安全的枚举
- 每个枚举常量都是一个对象。枚举常量用逗号分隔。
1 | enum class Direction { |
因为每一个枚举都是枚举类的实例,所以他们可以是这样初始化过的
1 | enum class Color(val rgb: Int) { |
覆盖基类的方法的匿名类
1
2
3
4
5
6
7
8
9
10
11enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
}; //使用分号(;)将成员定义中的枚举常量定义分隔开。与signal()分开
abstract fun signal(): ProtocolState
}枚举类中实现接口
枚举类可实现接口,但不能从类继承。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23enum class ConsoleColor(var argb : Int){
RED(0xFF0000){
override fun print() {
println("我是枚举常量 RED ")
}
},
WHITE(0xFFFFFF){
override fun print() {
println("我是枚举常量 WHITE ")
}
},
BLACK(0x000000){
override fun print() {
println("我是枚举常量 BLACK ")
}
};
abstract fun print()
}
fun main(args: Array<String>) {
ConsoleColor.BLACK.print() //我是枚举常量 BLACK
}属性方法
- 每个枚举常量都包含两个属性:
name
(枚举常量名)和ordinal
(枚举常量位置) - 提供了
values()
和valueOf()
方法来检测指定的名称与枚举类中定义的任何枚举常量是否匹配。 - 自 Kotlin 1.1起,可以使用
enumValues<T>()
和enumValueOf<T>()
函数以泛型的方式访问枚举类中的常量。 - 如果指定的名称与类中定义的任何枚举常量均不匹配,
valueOf()
方法将抛出IllegalArgumentException
异常。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19enum class Color(var argb : Int){
RED(0xFF0000),
WHITE(0xFFFFFF),
BLACK(0x000000),
GREEN(0x00FF00)
}
fun main(args: Array<String>) {
//name = RED ordinal = 0
println("name = " + Color.RED.name + "\tordinal = " + Color.RED.ordinal)
//RED, WHITE, BLACK, GREEN
println(enumValues<Color>().joinToString { it.name })
//RED
println(enumValueOf<Color>("RED"))
println(Color.valueOf("RED")) //RED
println(Color.values()[0]) //RED
}
- 每个枚举常量都包含两个属性:
五、内联类
内联类仅在 Kotlin 1.3 之后版本可用,目前还是实验性的。关于详情请参见内联类