final类
Kotlin中没有添加修饰符直接以class开头的都是final类。
构造函数
在Kotlin中,一个类有一个primary constructor,一个或多个secondary constructors。primary constructor 属于类头部分,它在类名之后。
class Person constructor(firstName: String) {
}
//constructor关键字可以被省略
class Person(firstName: String) {
}
primary constructor不能包含任何的代码,初始代码可以放在初始块中,初始化代码块以init关键字开头。
class Customer(firstName: String, lastName: String, email: String) {
var firstName: String
var lastName: String
var email: String
init {
this.firstName = firstName
this.lastName = lastName
this.email = email
}
}
也可以直接把primary constructor中的参数直接声明成为类的属性,定义的方法是在参数名前加上 var 或者 val 关键字,val 是代表属性是常量。在创建类的时候,调用构造函数就直接把它们进行了初始化,这样就不用在类中单独声明类的属性了。
class Customer(
var firstName: String,
var lastName: String,
var email: String)
如果希望类的构造器是私有的,就需要显示的指明。
class DontCreateMe private constructor () {
}
除了primary constructory,还有secondary constructors,同样也是constructor开头,位于类体中。 如果primary constructory和secondary constructors同时存在,每个secondary constructor需要委托到primary constructor中去,或者间接通过另一个secondary constructors来委托。使用方法就是使用this,表示委托到本类的另一个构造函数中。
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
open类
Kotlin中如果一个类需要能被继承,必须在类声明处使用open关键字,open关键字跟Java中的final是恰恰相反的。上面说过,Kotlin中一个类默认是public final的,final类自然是不能被继承。
在Kotlin中所以的类都有一个默认的父类Any,这个类似于Java中的Object。 Any不属于java.lang.Object,其只有三个方法:
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
重写规则
如果一个类继承自多个类,并且它的这些父类都有同一个可以重写的函数,那么这个类必须重写这个函数并且提供它自己的实现,另外在子类中指示父类的方法是super,A为父类类名,这样就可以指示不同的父类了。
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are 'open' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
上面类A和B都有f()函数可以重写,所以C类,必须重写这个函数f(),主要是为了消除歧义。
注意,在子类中被标记为override的成员函数本身也是可以被重新的,也就是open的,如果希望它禁止被重写,需要使用final关键字。
open class AnotherDerived() : Base() {
final override fun v() {}
}
属性重写也使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
open class Foo {
open val x: Int get { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
数据类
我们常常会创建一个只包含数据的类,其他什么事情都不做。 在Kotlin中,这样的类叫做数据类,表示关键字为data:
data class User(val name: String, val age: Int)
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- equals() / hashCode()
- toString() 格式如 “User(name=John, age=42)”
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数是深度复制 有时我们需要复制一个对象,修改部分属性,其他属性保持不变。此时就需要copy() 函数。
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
注意:如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。
data class User(val name: String = "", val age: Int = 0)
多元赋值(解构声明赋值)
data class Person(var name: String="zhangsan", var age: Int=23)
val person = Person ("zhangsan", 23)
val (name, age) = person
// 内部实现为
//val name = person.component1()
//val age = person.component2()
var (name,_)=Person()//不需要使用的变量用下划线代替
println(person.component1()) //打印zhangsan
println(name) //打印zhangsan
来一个更直接的例子:
class Pair<K, V>(val first: K, val second: V) {
operator fun component1(): K {
return first
}
operator fun component2(): V {
println("component2")
return second
}
}
fun main(vararg args:String)
{
val pair = Pair(first = 1, second = "one")
val (num, name) = pair
println("num = $num, name = $name")
}
Note that the componentN() functions need to be marked with the operator keyword to allow using them in a destructuring declaration.
现在对于Map的遍历我们可以像下面这样:
fun main(args: Array<String>) {
val map = hashMapOf<String, Int>()
map.put("one", 1)
map.put("two", 2)
for ((key, value) in map) {
println("key = $key, value = $value")
}
}
标准库提供了 Pair 和 Triple,数据类Pair来处理双元对数据,Triple用于三元数据。
var pair = Pair("aa", "bb")
var (arg1, arg2) = pair
println("${pair.first} + ${pair.second}")
//打印 aa + bb arg1等于pair.first,arg2等于pair.second
var pair = Pair("aa" to 0, "bb" to 1)
var (arg1, arg2) = pair
println("${arg1.first} + ${arg2.second}")
//打印 aa + 1
抽象类和接口
跟Java一样,使用abstract关键字。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
如果抽象类中含有抽象属性,再实现子类中必须将抽象属性初始化,除非子类也为抽象类。Kotlin与java一样是单继承
接口
Kotlin的接口类似于Java 8,它可以包含抽象方,以及方法的实现。
interface MyInterface {
fun bar() // 未实现
fun foo() { //已实现
// 可选的方法体
println("foo")
}
}
接口中的属性只能是抽象的,不允许初始化值,接口不会保存属性值,实现接口时,必须重写属性:
interface MyInterface{
var name:String //name 属性, 抽象的
}
class MyImpl:MyInterface{
override var name: String = "runoob" //重载属性
}
枚举类
枚举类最基本的用法是实现一个类型安全的枚举。
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
和Java一样,每一个枚举都是枚举类的实例,它们可以被初始化:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
密封类(Sealed Classe)
类名前面添加 sealed 修饰符。当一个值为有限集中的类型,而不能有其它类型时,可以用密封类来表示受限的类继承结构。
虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明。密封类从某种意义上说,它们是枚举类的扩展:枚举类型的值集也受到限制,但每个枚举常量仅作为单个实例存在,而密封类的子类可以包含多个实例并包含状态。这样又具备了枚举不具备的灵活性。
sealed class BaseClass {
class Test1 : BaseClass() {
override fun test() {
println("Test1实例")
}
}
class Test2 : BaseClass() {
override fun test() {
println("Test2实例")
}
}
object Test3 : BaseClass() {
override fun test() {
println("Test3实例")
}
}
open fun test() {
println("BaseClass实例")
}
}
fun test(instance: BaseClass)=when(instance){
is BaseClass.Test1-> instance.test()
is BaseClass.Test2-> instance.test()
is BaseClass.Test3->instance.test()
}
fun main(str: Array<String>) {
test( BaseClass.Test1() )
test( BaseClass.Test2() )
test( BaseClass.Test3 )
}
内部类
kotlin默认的内部类是final的,不能持有外部类的状态(属性、方法等)。用inner标记,可以访问外部成员。内部类对外部类的对象有一个引用。注意:外部类不可以访问内部类的 private 成员,在Java中却是可以访问的。
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val demo = Outer().Inner().foo() // == 1
当内部类不使用inner标记时,我们可以称之为嵌套类(静态的final的):
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo = Outer.Nested().foo() // == 2
可以用对象表达式(object)创建匿名内部类,对于函数式java接口,可以使用lambda表达式创建。
fun main(args: Array<String>) {
//对象表达式,对于接口中有一个或多个抽象方法都可用
val r = object : Runnable {
override fun run() {
println("main?")
}
}
//只用于函数式接口
val s = Runnable { println("s") }
r.run()
s.run()
}
object类
可以使用object关键字声明一种特殊的类,这个类只有一个实例,所以这个类里面的成员默认都是static的。
object Singleton {
private var num = 0
fun sequence(): Int {
num += 1
return num
}
}
Singleton.sequence()