Kotlin——面向对象(下)
扩展
Kotlin支持扩展方法和扩展属性
扩展方法
Kotlin的扩展其实就是定义一个函数,只是函数名不要写成简单的普通函数,而是在函数名前增加被扩展的类(接口)名和点号“.”
fun main(args: Array<String>) {
//row
var r = Row()
r.test()
//调用扩展方法
r.extendsFun("hello")
//子类调用父类的方法
var sr = SubRow()
sr.test()
//子类调用自己的方法
sr.sub()
//子类调用父类的扩展方法
sr.extendsFun("sub")
}
open class Row{
fun test(){
println("Row的test方法")
}
}
class SubRow: Row() {
fun sub() {
println("sub方法")
}
}
fun Row.extendsFun(name: String) {
println("扩展的Row方法")
}
- 程序为一个类扩展了方法之后,就像为该类增加了方法一样,所有的该类的对象都可以调用扩展的方法,该类的子类实例也可以调用扩展的方法。
扩展属性
Kotlin允许扩展属性,Kotlin扩展属性其实是通过添加getter、setter方法实现的。扩展的属性只能是计算属性
final和open修饰符
- final关键字可用于修饰类、属性和方法,表示它修饰的类、属性和方法不可改变
Kotlin会为非抽象类自动添加final修饰符,也会为非抽象方法、非抽象属性等无须重写的成员自动添加final修饰符。开发者可以使用open修饰符取消自动添加final修饰符
Kotlin的final修饰符不能修饰局部变量,open自然也不能修饰局部变量
可执行“宏替换”的常量
Kotlin提供了const用来修饰可执行“宏替换”的常量,这种常量也被称为"编译时"常量。因为它在编译阶段就会被替换掉
- 位于顶层或是对象表达式的成员
- 初始值为基本类型值或字符串字面值
- 没有自定义的getter方法
const val MAX_AGE =100
println(MAX_AGE)
上面的代码中,根本就不存在MAX_AGE,当程序执行println时,实际替换为执行println(100),这个替换在编译阶段就完成了
final属性
final属性表明该属性不能被重写。如果程序对属性不使用任何修饰符,Kotlin会自动为该属性添加final修饰
final方法
使用final修饰的方法不可被重写。如果程序不为方法添加任何修饰,Kotlin会自动为该方法添加final修饰
final类
使用final修饰的类不可以有子类,Kotlin也会为没有显式使用open修饰的类添加final修饰
抽象类
抽象成员和抽象类
抽象成员(方法和属性)和抽象类必须使用abstract修饰符来定义,包含抽象成员的类只能被定义成抽象类。抽象类中可以没有抽象成员
规则如下
- 抽象类必须使用abstract修饰符来修饰,抽象成员也必须使用abstract修饰符来修饰,抽象方法不能有方法体
- 抽象类不能被实例化,无法调用抽象类的构造器创建抽象类的实例
- 抽象类可以包含属性、方法、构造器、初始化块、嵌套类5种成员,抽象类的构造器不能用于创建实例,主要用于被其子类调用
- 含有抽象成员的类只能被定义成抽象类
定义抽象方法,只需在普通方法上增加abstract修饰符,并把普通方法的方法体去掉即可
定义抽象类,只需在普通类上增加abstract修饰符
abstract class Shape{
init {
println("shape的初始化块")
}
var color = ""
abstract fun calPerimeter():Double
//抽象属性不需要初始化
abstract val type:String
constructor()
constructor(color:String){
println("shape构造器")
this.color = color
}
}
创建一个普通类继承抽象类
class Triangle(color:String, private var a:Double):Shape(color){
override fun calPerimeter(): Double {
this.a = a
return a * a * a
}
override val type: String = "三角形";
}
fun main(args: Array<String>) {
var triangle = Triangle("red", 1.0)
triangle.calPerimeter()
}
密封类
密封类的子类是固定的,密封类的子类必须与密封类本身在同一个文件中,在其他文件中则不能为密封类派生子类
//创建一个密封类
sealed class Appleb{
abstract fun eat()
}
//创建密封类的子类
open class RedFuJi:Appleb(){
override fun eat() {
println("密封类的子类")
}
}
fun main(args: Array<String>) {
val a1:Appleb = RedFuJi()
a1.eat()
}
接口
接口的定义
语法
[修饰符] interface 接口名:父接口1,父接口2...{
//零个到多个属性定义...
//零个到多个方法定义...
//零个到多个嵌套类、嵌套接口、嵌套枚举定义...
}
- 修饰符可以是public|internal|private中的任意一个或者完全省略,完全省略默认采用public
- 接口名应与类名采用相同的命名规则
- 一个接口可以有多个直接父接口,但接口只能继承接口
Kotlin的接口既可以包含抽象方法,也可以包含非抽象方法
接口中不能包含构造器和初始化块定义
interface Outputtable{
//定义属性
//只读属性 定义get方法
val name:String
get() ="只读属性"
//读写属性 设置get set方法
var price:Int
get() {
return price
}
set(value) {
price = value
}
//抽象属性
var age:Int
//抽象方法
fun a1()
fun a2()
fun a3()
//非抽象方法
fun print(vararg msg: String){
for (s in msg) {
println(s)
}
}
}
接口的继承
接口完全支持多继承,一个接口可以有多个父接口,一个接口继承多个父接口,多个父接口排在英文冒号:之后,它们之间以英文逗号隔开
使用接口
接口的主要用途就是被实现类实现
class OutputtableImpl:Outputtable{
override var age: Int
get() = TODO("Not yet implemented")
set(value) {}
override fun a1() {
TODO("Not yet implemented")
}
override fun a2() {
TODO("Not yet implemented")
}
override fun a3() {
TODO("Not yet implemented")
}
}
嵌套类和内部类
把一个类放在另一个类的内部定义,这个定义在其他类内部的类就称为嵌套类,包含嵌套类的类就被称为外部类
- 嵌套类(相当于静态内部类)将一个类放在另一个类中定义,这个类就是嵌套类,相当于Java的静态内部类
- 内部类(非静态内部类)使用inner修饰的嵌套类叫内部类,相当于Java中无static修饰的非静态内部类
内部类
内部类相当于外部类的实例成员,因此内部类可以直接访问外部类的所有成员
class Outer(var name:String){
fun sayName(){
println("name${name}")
}
inner class innerCls(){
var innerField = ""
fun sayInnerName(){
println("sayInner+${innerField}")
//访问外部类属性
name
sayName()
}
}
}
fun main() {
//创建外部类
var outer = Outer("hh")
//外部类访问属性
println(outer.name)
outer.sayName()
//创建内部类对象
var innerCls = outer.innerCls()
//访问内部类属性
println(innerCls.innerField)
innerCls.sayInnerName()
}
- 如果内部类属性与外部类属性同名,则可通过使用this、带标签的thiis进行限定区分
class Out{
var name:String = ""
class inner{
var name:String = ""
fun info(){
//访问外部类name属性
this@Out.name
//访问内部类name属性
this.name
}
}
}
- 内部类成员可以访问外部类的private成员,但是外部类不可访问内部类的成员,内部类的成员只在内部类范围内是可知的。如果外部类需要访问内部类的成员,则必须显式创建内部类对象来调用访问其成员
嵌套类
嵌套类相当于Java的静态内部类,嵌套类属性外部类本身,不是外部类实例相关
嵌套类不能访问外部类的其他成员,只能访问另一个嵌套类
- 外部类不能直接访问嵌套类的成员,但可以使用嵌套类的对象作为调用者来访问嵌套类的成员
匿名内部类
Kotlin提供了对象表达式来表示内部类
object[:0~N个父类型]{
//对象表达式的类体部分
}
- 对象表达式不能是抽象类
- 对象表达式不能定义构造器,但可以定义初始化块
- 对象表达式可以包含inner修饰的类,不能包含嵌套类
interface Outputable{
fun a(){}
}
abstract class Product(var price:Double){
abstract val name:String
abstract fun proInfo()
}
fun main() {
//创建一个父对象的对象表达式
var ob1 = object : Outputable {
//重写接口中的方法
override fun a() {
println("a 方法被重写")
}
}
ob1.a()
//创建零个父对象的对象表达式
var ob2 = object {
init {
println("ob2 init")
}
//属性
var name = "ob2 field"
//方法
fun test(){
println("ob2 fun")
}
}
ob2.test()
//创建两个父类型的对象表达式
val ob3 = object : Outputable,Product(33.3) {
override val name: String = "ob3 name"
override fun proInfo() {
println("ob3 proInfo")
}
}
ob3.proInfo()
}
对象声明和单例模式
对象声明的语法格式如下:
object ObjectName[:0~N个父类型]{
//对象表达式的类体部分
}
- 对象声明需要在object关键字后面指定名字
- 对象表达式是一个表达式,它可以被赋值给变量;对象声明不是表达式,不能用于赋值
- 对象声明可包含嵌套类,不能包含内部类。对象表达式可包含内部类,不能包含嵌套类
- 对象声明不能定义在函数和方法内,对象表达式可嵌套在其他对象声明或非内部类中
枚举类
Kotlin使用enum class关键字组合定义枚举类。枚举类是一种特殊的类,它一样可以有自己的属性、方法,可以实现一个或多个接口,也可以定义自己的构造器
- Kotlin枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承Kotlin.Enum类,而不是默认继承Any类,因此枚举类不能显式继承其他父类
- 使用enum定义的非抽象枚举类不能使用open修饰,枚举类不能派生子类
- 枚举类的构造器只能使用private访问控制符。
- 枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例。
枚举类默认提供两个方法 - EnumClass.valueOf(value:String):EnumClass:类似于Java枚举类的valueOf()方法,用于根据枚举的字符串名获取实际的枚举值
- EnumClass.values():Array<EnumClass>:类似于Java枚举类的values()方法,用于获取该枚举类的所有枚举值组成的数组