Kotlin语言学习笔记(3)
数据类(Data Classes)
data class User(val name: String, val age: Int)
编译器自动生成的有:
- equals()/hashCode()
- toString() 形式为 "User(name=John, age=42)"
- componentN() 函数
- copy() 函数
数据类的
- equals(), hashCode(), toString() 可以自定义
- 自动生成的 componentN() 可以覆盖基类(非数据类)的 componentN()
- componentN() and copy() 不可以自定义
数据类必须符合以下条件。
- 主体构造器必须至少有一个参数。
- 所有主体构造器的参数都必须被标注为var或val。
- 数据类不可以是 abstract, open, sealed 或 inner。
// 缺省参数的例子。
data class User(val name: String = "", val age: Int = 0)
// 主体构造器中定义的属性会自动生成相应函数
// 数据类内定义的属性不会自动生成相应函数
data class Person(val name: String) {
var age: Int = 0
}
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20
/*
person1 == person2: true
person1 with age 10: Person(name=John)
person2 with age 20: Person(name=John)
*/
// 自动生成的copy函数
// fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
// 实际应用copy函数的代码
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
// 数据类与解构
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
标准数据类包括Pair
和Triple
。
密封类和密封接口(Sealed Classes)
密封类(接口)是指在编译期内直接子类或子接口均确定的类(接口)。
密封类(接口)对继承有限制,不能在密封类(接口)所在模块之外定义密封类的直接子类或子接口。
// 使用 sealed 关键字定义密封类和密封接口
sealed interface Error
sealed class IOError(): Error
class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()
object RuntimeError : Error
// 密封类也是抽象类,密封类的构造器只有两种可见性,protected 或 private。
sealed class IOError {
constructor() { /*...*/ } // protected by default
private constructor(description: String): this() { /*...*/ } // private is OK
// public constructor(code: Int): this() {} // Error: public and internal are not allowed
}
// 密封类(接口)的直接子类或子接口必须定义在同一个包内
// 非直接子类可以定义在任何地方
sealed interface Error // has implementations only in same package and module
sealed class IOError(): Error // extended only in same package and module
open class CustomError(): Error // can be extended wherever it's visible
// 使用密封类的好处在于在when表达式中可以处理所有情况,不必用else。
fun log(e: Error) = when(e) {
is FileReadError -> { println("Error while reading file ${e.file}") }
is DatabaseError -> { println("Error while reading from database ${e.source}") }
is RuntimeError -> { println("Runtime error") }
// the `else` clause is not required because all the cases are covered
}
嵌套类(Nested Classes)
// 嵌套类
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
// 类和接口的相互嵌套
interface OuterInterface {
class InnerClass
interface InnerInterface
}
class OuterClass {
class InnerClass
interface InnerInterface
}
// 内部类(Inner Classes)
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
// 匿名内部类(Anonymous Inner Classes)
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { ... }
override fun mouseEntered(e: MouseEvent) { ... }
})
// 函数型接口的对象生成
val listener = ActionListener { println("clicked") }
枚举类(Enum Classes)
每个枚举常量都是一个对象。
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
// 初始化
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
// 枚举类内部的匿名类
// 在枚举类你内定义抽象方法
// 然后使用枚举对象的匿名类来实现抽象方法
// 这里需要使用分号隔开枚举对象的定义和抽象方法的定义
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
// 枚举类不能继承别的类,但是可以实现接口
// 所有枚举类均已实现 Comparable 接口
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
PLUS {
override fun apply(t: Int, u: Int): Int = t + u
},
TIMES {
override fun apply(t: Int, u: Int): Int = t * u
};
override fun applyAsInt(t: Int, u: Int) = apply(t, u)
}
/*
PLUS(13, 31) = 44
TIMES(13, 31) = 403
*/
// 使用枚举常量
// EnumClass.valueOf(value: String): EnumClass
// EnumClass.values(): Array<EnumClass>
// enumValues<T>()
// enumValueOf<T>()
enum class RGB { RED, GREEN, BLUE }
inline fun <reified T : Enum<T>> printAllValues() {
print(enumValues<T>().joinToString { it.name })
}
printAllValues<RGB>() // prints RED, GREEN, BLUE
// 每个枚举常量都有如下属性
val name: String
val ordinal: Int
对象表达式(Object Expressions)
对象表达式扩展了Java中的匿名内部类。
// 不带基类的对象表达式
val helloWorld = object {
val hello = "Hello"
val world = "World"
// object expressions extend Any, so `override` is required on `toString()`
override fun toString() = "$hello $world" // Hello World
}
// 带基类的对象表达式
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
// 构造器带参数的类也能使用对象表达式
open class A(x: Int) {
public open val y: Int = x
}
interface B { /*...*/ }
val ab: A = object : A(1), B {
override val y = 15
}
// 对象表达式可以达成匿名类(对象)的效果
class C {
private fun getObject() = object {
val x: String = "x"
}
fun printX() {
println(getObject().x)
}
}
// 匿名对象只能用于local和private声明
// 如果用于public声明,则成员不可访问
interface A {
fun funFromA() {}
}
interface B
class C {
// The return type is Any. x is not accessible
fun getObject() = object {
val x: String = "x"
}
// The return type is A; x is not accessible
fun getObjectA() = object: A {
override fun funFromA() {}
val x: String = "x"
}
// The return type is B; funFromA() and x are not accessible
fun getObjectB(): B = object: A, B { // explicit return type is required
override fun funFromA() {}
val x: String = "x"
}
}
// 对象表达式中的方法可以访问外层变量(不仅限于final变量)
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ...
}
对象声明(Object declarations)
使用对象声明可以方便的声明单件(Singleton)。
在类中使用对象声明可以声明类的伴生对象(Companion object)。
伴生对象中声明的方法相当于其他语言的静态方法。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
// 使用单件
DataProviderManager.registerDataProvider(...)
// 有基类的情况
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { ... }
override fun mouseEntered(e: MouseEvent) { ... }
}
// 类的伴生对象,用于实现静态方法
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
// 使用伴生对象的方法(相当于静态方法)
val instance = MyClass.create()
// 使用伴生对象自身
class MyClass {
companion object { }
}
val x = MyClass.Companion
// 类名本身可以被用作其伴生对象的引用
class MyClass1 {
companion object Named { }
}
val x = MyClass1
class MyClass2 {
companion object { }
}
val y = MyClass2
// 伴生对象可以实现接口
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
val f: Factory<MyClass> = MyClass
对象表达式和对象声明的语义
- 对象表达式是贪婪的(使用时即刻生成)。
- 对象声明是懒惰的 (第一次访问时生成)。
- 伴生对象在相应的类被生成时生成。