您当前的位置: 首页 >  kotlin

Kevin-Dev

暂无认证

  • 0浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Kotlin -- 知识点】继承类

Kevin-Dev 发布时间:2022-03-15 18:39:46 ,浏览量:0

文章目录
    • 一、面向对象的特征
    • 二、Kotlin 继承类
      • 1. 超类(Any)
      • 2. 定义
      • 3. 函数的重载与重写
      • 4. 重写属性
      • 5. 覆盖规则

众所周知,Kotlin 是一门面向对象的开发语言。也有着面向对象语言的特性。而面向对象的三大特性即封装、继承、多态。

一、面向对象的特征

面向对象的三大特征:封装、继承、多态

二、Kotlin 继承类 1. 超类(Any)

在 Kotlin 中,所有的类都是继承于 Any 类,这是这个没有父类型的类。即当我们定义各类时,它默认是继承于 Any 这个超类的

Any 这个类只是给我们提供了 equals()、hashCode()、toString() 这三个方法。

源码分析

package kotlin
/** 
 * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass. 
 * 看这个源码注释:意思是任何一个Kotlin的类都继承于这个[Any]类 
 */
 public open class Any {    
 	// 比较: 在平时的使用中经常用到的equals()函数的源码就在这里额    
 	public open operator fun equals(other: Any?): Boolean    

	// hashCode()方法:其作用是返回该对象的哈希值    
	public open fun hashCode(): Int    

	// toString()方法    
	public open fun toString(): String
}

从源码可以我们看出,它直接属于 kotlin 这个包下。并且只定义了上面所示的三个方法。或许你具有 Java 的编程经验。在我们熟知的 Java 中,所有的类默认都是继承于 Object 类型的。而 Object 这个类除了比 Any 多了几个方法与属性外,没有太大的区别。不过他们并不是同一个类。

2. 定义

2.1 继承类的基础使用

定义继承类的关键字为:open。不管是类、还是成员都需要使用 open 关键字。

定义格式:

open class 类名{
     ...
     open var/val 属性名 = 属性值
     ...
     open fun 函数名()
     ...
}

例:这里定义一个继承类 Demo,并实现两个属性与方法,并且定义一个 DemoTest 去继承自 Demo

open class Demo{

    open var num = 3

    open fun foo() = "foo"

    open fun bar() = "bar"

}

class DemoTest : Demo(){
    // 这里值得注意的是:Kotlin使用继承是使用`:`符号,而Java是使用extends关键字
}

fun main(args: Array) {

    println(DemoTest().num)
    DemoTest().foo()
    DemoTest().bar()

}

输出结果:

3 foo bar

2.2 继承类的构造函数

  • 无主构造函数 当实现类无主构造函数时,则每个辅助构造函数必须使用super关键字初始化基类型,或者委托给另一个构造函数。 请注意,在这种情况下,不同的辅助构造函数可以调用基类型的不同构造函数

例:这里举例在Android中常见的自定义View实现,我们熟知,当我们指定一个组件是,一般实现继承类(基类型)的三个构造函数。

class MyView : View(){    
	constructor(context: Context) : super(context)    

	constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)    

	constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}
  • 存在主构造函数 当存在主构造函数时,主构造函数一般实现基类型中参数最多的构造函数,参数少的构造函数则用 this 关键字引用即可了。

例:同样以自定义组件为例子

class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)    
	: View(context, attrs, defStyleAttr) { 
   	constructor(context: Context?) : this(context,null,0)    
   	
   	constructor(context: Context?,attrs: AttributeSet?) : this(context,attrs,0)
}
3. 函数的重载与重写

在 Kotlin 中关于函数的重载与重写,和 Java 中是几乎是一样的,但是这里还是举例来说明一下。

3.1 重写函数中的两点特殊用法

  • 当基类中的函数,没有用 open 修饰符修饰的时候,实现类中出现的函数的函数名不能与基类中没有用 open 修饰符修饰的函数的函数名相同,不管实现类中的该函数有无 override 修饰符修饰。直接看例子你就明白了。

例子:

open class Demo{
    fun test(){}   // 注意,这个函数没有用open修饰符修饰
}

class DemoTest : Demo(){    
	// 这里声明一个和基类型无open修饰符修饰的函数,且函数名一致的函数    
	// fun test(){}   编辑器直接错误,根本无法运行程序    
	// override fun test(){}   同样错误
}
  • 当一个类不是用 open 修饰符修饰时,这个类默认是 final 的。即:
class A{}

等价于

final class A{}   // 注意,`final`修饰符在编辑器中是灰色的,因为Kotlin中默认的类默认是final的

3.2 方法重载

open class Demo{
    open fun foo() = "foo"
}

class DemoTest : Demo(){

    fun foo(str: String) : String{
        return str
    }

	override fun foo(): String {
        return super.foo()
    }
} 

fun main(args: Array) {
    println(DemoTest().foo())
    DemoTest().foo("foo的重载函数")
}   

输出结果:

foo

foo的重载函数

4. 重写属性
  • 重写属性和重写方法其实大致是相同的,但是属性不能被重载。
  • 重写属性即指:在基类中声明的属性,然后在其基类的实现类中重写该属性,该属性必须以 override 关键字修饰,并且其属性具有和基类中属性一样的类型。且可以重写该属性的值(Getter)

例子:

open class Demo{
    open var num = 3
}

class DemoTest : Demo(){
    override var num: Int = 10
}

4.1 重写属性中,val 与 var 的区别 这里可以看出重写了 num 这个属性,并且为这个属性重写了其值为 10 但是,还有一点值得我们注意:当基类中属性的变量修饰符为 val 的使用,其实现类可以用重写属性可以用 var 去修饰。反之则不能。

例子:

open class Demo{
    open val valStr = "我是用val修饰的属性"
}

class DemoTest : Demo(){

    /*
     * 这里用val、或者var重写都是可以的。
     * 不过当用val修饰的时候不能有setter()函数,编辑器直接会报红的
     */

	// override val valStr: String
    //   get() = super.valStr

    // override var valStr: String = ""
    //   get() = super.valStr

	// override val valStr: String = ""

    override var valStr: String = "abc"
        set(value){field = value}
}

fun main(arge: Array>){    
	println(DemoTest().valStr)    
	
	val demo = DemoTest()    
	demo.valStr = "1212121212"    
	println(demo.valStr)
}

输出结果:

abc 1212121212

4.2 Getter() 函数慎用 super 关键字

class DemoTest : Demo(){    
	/*     
	 * 这里介绍重写属性是,getter()函数中使用`super`关键字的情况     
	 */    
	override var valStr: String = "abc"        
	get() = super.valStr        
	set(value){field = value}
}

fun main(arge: Array>){    
	println(DemoTest().valStr)    
	val demo = DemoTest()    
	demo.valStr = "1212121212"    
	println(demo.valStr)
}

输出结果:

我是用val修饰的属性

我是用val修饰的属性

注意:重写属性的时候慎用 super 关键字

4.3 在主构造函数中重写

class DemoTest2(override var num: Int, override val valStr: String) : Demo()

fun main(args: Array){    
	val demo2 = DemoTest2(1,"构造函数中重写")     
	println("num = ${demo2.num} \t valStr = ${demo2.valStr}")
}

输出结果:

num = 1 valStr = 构造函数中重写

5. 覆盖规则

这里的覆盖规则,是指实现类继承了一个基类,并且实现了一个接口类,当我的基类中的方法、属性和接口类中的函数重名的情况下,怎样去区分实现类到底实现哪一个中的属性或属性。 这一点和一个类同时实现两个接口类,而两个接口都用同样的属性或者函数的时候是一样的

open class A{
    open fun test1(){ println("基类A中的函数test1()") }

    open fun test2(){println("基类A中的函数test2()")}
}

interface B{
    fun test1(){ println("接口类B中的函数test1()") }

    fun test2(){println("接口类B中的函数test2()")}
}

class C : A(),B{
    override fun test1() {
        super.test1()
        super.test1()
    }

	override fun test2() {
        super.test2()
        super.test2()
    }
}
关注
打赏
1658837700
查看更多评论
立即登录/注册

微信扫码登录

0.0513s