Kotlin学习(七): 类和继承、接口与实现
来源:互联网 发布:天天象棋辅助软件 编辑:程序博客网 时间:2024/06/06 04:44
本文是学习Kotlin的类和继承相关,嵌套类相关,接口相关。
类
Kotlin的类的声明与Java一样,使用class
关键字
class Invoice {}
类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹。类头和类体是可选的;如果没有类体可以省略大括号。
class Empty
构造函数
Kotlin的类有两种构造函数,一种是主构造函数,一种是二级构造函数。
主构造函数是写在类名后面(可以有可选的类型参数),使用constructor
关键字声明
class Person constructor(firstName: String) {}
如果一个非抽象类没有声明构造函数(主构造函数或二级构造函数),它会产生一个没有参数的构造函数。构造函数是 public
。如果你不想类有公共的构造函数,就得声明一个空的主构造函数
class DontCreateMe private constructor () {}
如果主构造函数没有任何注释或可见性修饰符,则可以省略constructor
关键字
class Person(firstName: String) {}
主构造函数不包含任何代码,初始化代码应该放在以init
做前缀的初始化块中
class Customer(name: String) { init { logger.info("Customer initialized with value ${name}") }}
主构造函数的参数既可以用在初始化块中,也可以用在类的属性初始化声明处
class Customer(name: String) { val customerKry = name.toUpperCase()}
一般用Java写构造函数的时候,里面是写属性的初始化,如下面
class Person { private String firstName; private String lastName; private int age; Person(String firstName, String lastName, int age) { this.firstName = firstName; this.lastName = lastName; this.age = age; }}
Kotlin有更简洁的写法,可以在主构造函数重声明属性并在主构造函数中初始化,同时可以设置属性为可变(var
)或者设置为只读(val
)
class Person(val firstName: String, val lastName: String, var age: Int){}
如果构造函数具有注解(@Inject
)或可见性修饰符(public
),则constructor
关键字必须写上,并且写在可见性和注解的后面
class Customer public @Inject constructor(name: String) { }
二级构造函数
二级构造函数同样是以constructor
关键字来声明,与主构造函数不同的是,二级构造函数是写在类里面,同时,二级构造函数可以有多个。
class Person { constructor(parent: Person) { parent.children.add(this) } constructor(parent: Person, count: Int) { parent.children.add(this) }}
如果该类有主构造函数,那么声明二级构造函数都要使用this
关键字来代理(Delegation)主构造函数
class Person(val name: String) { constructor(name: String, paret: Person) : this(name) { parent.children.add(this) } constructor(name: String, parent: Person, count: Int) : this(name) { parent.children.add(this) }}
注意:在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。
class Customer(val customerName: String = "")
创建类的实例
Kotlin创建类的实例不需要像Java一样使用到new
关键字,直接调用构造函数即可创建类的实例
val invoice = Invoice()val customer = Customer("Joe Smith")
类成员
Kotlin的类里面可以包含
- 构造函数和初始化块(Constructors and initializer blocks
)
- 函数(Functions
)
- 属性(Properties
)
- 嵌套类和内部类(Nested and Inner Classes
)
- 数据对象(Object Declarations
)
继承
Kotlin中的所有类都有一个公共的父类Any
class Example // 隐式继承自Any
Any
不是java.lang.Object
,Any
只包含equals()
,hashCode()
以及toString()
三个成员
如何继承一个类,声明一个类的父类呢,在类后面加冒号然后再加父类
open class Base(p: Int)class Derived(p: Int) : Base(p)
注意一个类如果要被别的类继承,要加上
open
关键字,在类名的最前面,默认情况下,Kotlin中的所有类都是final
的,Kotlin的接口以及成员默认都是open
。
同样,如果父类有主构造函数,则子类必须在主构造函数中初始化
/*** 父类*/open class BaseKot(name: String) {}/*** 子类*/class Kot(name: String) : BaseKot(name) {}
如果子类没有主构造函数,则可以在二级构造函数中使用super
关键字初始化或者在代理(Delegation)另一个构造函数。
/*** 父类*/open class BaseKot(name: String) {}/*** 子类*/class Kot : BaseKot { constructor(name: String) : super(name) { }}
下面这种情形中不同的二级构造函数可以调用父类不同的构造方法
class MyView : View { constructor(ctx: Context) : super(ctx) { } constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) { }}
重写方法
上面说了,如果一个类要被继承需要在父类的类名前面加上open
关键字,该类才能被继承,同样,一个父类的方法如果要能被重写,也需要加上open
关键字,如果没有加的话,子类是不能重写改方法的。
/*** 父类*/open class BaseKot { open fun v() {} fun nv() {}}/*** 子类*/class Kot : BaseKot() { // 加final可以设置不被重写 override fun v() { super.v() } // 重写nv方法会报错,加override也不行,只有父类的nv方法设置为open才行 // fun nv() { }}
同样,如果类不是
open
的话,里面的成员也不允许设置为open
重写属性
重写属性与重写方法类似,并且属性必须具有兼容类型,可以将val
属性重写成var
,也可以将var
属性重写成val
/*** 父类*/open class BaseKot { open val age: Int get() = 0 // get() = 0 与 = 0相同 open fun v() {}}/*** 子类*/class Kot : BaseKot() { override var age: Int = 5 // 重写为var类型 override fun v() { super.v() }}
可以在子类的主构造函数中声明override
class Kot(override var age: Int) : BaseKot() { override fun v() { super.v() }}
使用super
实现
子类中的代码可以使用super
关键字调用其父类的函数与属性访问器的实现:
open class Foo { open fun f() { println("Foo.f()") } open val x: Int get() = 1}class Bar : Foo() { override fun f() { super.f() println("Bar.f()") } override val x: Int get() = super.x + 1}
在一个内部类中访问外部类的父类,可以通过由外部类名限定的super
关键字来实现:super@Outer
:
class Bar : Foo() { override fun f() { /* …… */ } override val x: Int get() = 0 inner class Baz { fun g() { super@Bar.f() // 调用 Foo 实现的 f() println(super@Bar.x) // 使用 Foo 实现的 x 的 getter } }}
重写规则
在Kotlin中,实现继承通常遵循如下规则:如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须复写这个成员并且提供自己的实现,要使用父类中提供的方法,用super<Base>
来表示
open class A { open fun f() { print("A") } fun a() { print("a") } // 不会被重写}interface B { fun f() { print("B") } // 接口成员默认是open的 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
中继承方法,而且C
继承a()
或b()
的实现没有任何问题,因为它们都只有一个实现。但是f()
有两个实现,所以要在C
中必须复写f()
并且提供自己的实现。
抽象类
与Java类似,Kotlin的类或者成员都可以被声明为abstract
,同时不需要添加open
关键字。
可以用一个抽象成员去复写一个带 open 注解的非抽象方法
open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()}
枚举类
Kotlin也支持枚举,每个枚举常量都是一个对象。
enum class Direction { NORTH, SOUTH, WEST, EAST}
初始化
由于每个枚举都是枚举类的实例,因此可以进行初始化
enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF)}
匿名类
枚举常量也可以使用对应的方法声明自己的匿名类,以及覆盖基本方法。
注意,与Java一样,如果枚举类定义了任何成员,则需要使用分号将成员定义中的枚举常量定义分隔开。
enum class ProtocolState { WAITING { override fun signal() = TALKING }, TALKING { override fun signal() = WAITING }; abstract fun signal(): ProtocolState}
使用枚举常量
就像在Java中一样,Kotlin中的枚举类有合成方法,允许列出定义的枚举常量,并通过名称获取枚举常量。这些方法的签名如下(假设枚举类的名称是EnumClass):
EnumClass.valueOf(value: String): EnumClass
EnumClass.values(): Array<EnumClass>
如果指定的名称与类中定义的任何枚举常量不匹配,该valueOf()
方法将抛出IllegalArgumentException
。
每个枚举常量都具有在枚举类声明中获取其名称和位置的属性:
val name: String
val ordinal: Int
密封类
密封类是用来表示受限的类层次结构。例如当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
密封类是类名前加sealed
关键字来声明
sealed class Expr { class Const(val number: Double) : Expr() class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber : Expr()}
密封类子类的扩展可以在任何地方,不必在密封类声明内部进行
使用密封类的最主要的的好处体现在使用when
表达式。可以确保声明可以覆盖到所有的情形,不需要再使用 else
情形。
fun eval(expr: Expr): Double = when(expr) { is Const -> expr.number is Sum -> eval(expr.e1) + eval(expr.e2) NotANumber -> Double.NaN // 前面说过,使用when的时候除非能把所有可能的情况都列举出来,不然一定要加上else // 这里就不需要,因为列举了所有的情况}
嵌套类
与Java一样,一个类中可以嵌套其他的类
class Outer { private val bar: Int = 1 class Nested { fun foo() = 2 }}
如何调用
val demo = Outer.Nested().foo() //==2
内部类
内部类是使用inner
关键字来声明。
class Outer { private val bar: Int = 1 inner class Inner { fun foo() = bar }}
如何调用
val demo = Outer().Inner().foo() // == 1
嵌套类与内部类的区别是:嵌套类不能够访问外部成员,因为内部类包含对外部类的对象的引用,所以能够访问外部类的成员;调用的时候,内部类需要使用外部类的实例来调用。
接口
Kotlin的接口类似与Java 8,都可以包含抽象方法,以及方法的实现。和抽象类不同的是,接口不能保存状态。可以有属性但必须是抽象的,或者提供访问器(getter)的实现。
接口的定义同样是使用interface
关键字
interface MyInterface { fun bar() fun foo() { // 函数体是可选的,自己实现方法 }}
实现接口
一个类或对象可以实现一个或多个接口
class Child : MyInterface, YouInterface { override fun bar() { // body }}
接口中的属性
interface MyInterface { val prop: Int // abstract val propertyWithImplementation: String get() = "foo" // 提供访问器getter,可以直接获取propertyWithImplementation的值foo fun foo() { print(prop) }}class Child : MyInterface { override val prop: Int = 29 override val propertyWithImplementation: String = "123"}
解决重写冲突
同类的重写类似,接口也一样,如果父类中声明了许多类型,有可能出现一个方法的多种实现,必须复写这个成员并且提供自己的实现,要使用父类中提供的方法,用super<Base>
来表示
interface A { fun foo() { print("A") } fun bar()}interface B { fun foo() { print("B") } fun bar() { print("bar") }}class C : A { override fun bar() { print("bar") }}class D : A, B { override fun foo() { super<A>.foo() super<B>.foo() }}
上面的代码是A
,B
接口都有声明了foo()
,bar()
函数,都实现了foo()
方法,但只有B
实现了bar()
,bar()
在A
中并没有声明它是抽象的,C
实体类需要重写并实现bar()
,而D
类,不用重写bar()
方法,因为继承的B
接口已经实现了。由于继承了两个foo()
的实现,所以用super<Base>
来区分。
- Kotlin学习(七): 类和继承、接口与实现
- Android开发,Kotlin的了解与学习(七)-----接口与实现
- Kotlin类、继承、接口实现
- Kotlin for Android(六)Kotlin类的继承和接口
- 类和接口的继承与实现
- kotlin 学习记录 基本语法 (4 类和继承)
- Kotlin学习笔记(三)类和继承
- Kotlin 学习笔记(2)类和继承
- Kotlin学习之-5.1 类和继承
- Kotlin 学习笔记(三) 类和继承
- Kotlin 类与继承
- Kotlin学习(3)类,对象和接口
- Kotlin-类和继承
- Kotlin 类和继承
- Kotlin-类和继承
- Kotlin for Android(七)Kotlin数据类和Gson
- Kotlin 学习之类和继承
- Kotlin学习(四)—— 类和对象,继承,覆盖,抽象类,属性和字段,接口,可见性修饰符,扩展
- 用户需求变更和确认的注意事项
- 数据库视频总结
- jsp生命周期
- Pandas数据存储
- Kotlin实战(一): 写一个登录的Demo
- Kotlin学习(七): 类和继承、接口与实现
- 【Java-并发】对象的线程安全性
- Kotlin学习(八): 对象表达式和声明(Object Expressions and Declarations)
- 654. Maximum Binary Tree
- java.lang.Integer
- Kotlin学习(九): 扩展(Extensions)和This表达式
- Kotlin学习(十): 可见性修饰符和数据类
- JDBC配置
- Kotlin学习(十一): 泛型(Generics)