在前面的章节中,详细的详解了类
的使用,可是因为篇幅的限制,关于类的不少特性都没有讲解到。今天在这篇文章中,详细的讲解Kotlin
中类的特性。若是您对Kotlin
中的类尚未一个总体的了解的话,请参见我上一篇文章Kotlin——中级篇(一):类(class)详解
众所周知,Kotlin
是一门面向对象的开发语言。那么他也有面向对象语言的特性。而面向对象的三大特性即封装
、继承
、多态
。这是每一门面向对象语言否具备的特性。今天这一节会着重的讲解Kotlin的继承
与Java
的不一样处和Kotlin
独有的特色。html
面向对象的三大特征:
封装
、继承
、多态
git
因为面向对象的三大特征太过于普通,并且这并非Kotlin
中特有的知识。在这里就很少作描述。github
在
Kotlin
中,继承这个特性除了定义关键字
,以及全部的父类和Java
语言不通以外,其余的其实无太大的差异。不过既然写到了这里,仍是从始至终的写完这个特性,若是您有Java
的基础,您能够当复习一遍。编程
Any
)在
Kotlin
中,说有的类都是继承与Any
类,这是这个没有父类型的类。即当咱们定义各种时,它默认是继承与Any
这个超类的编辑器
例:ide
class Demo // 这里定义了一个Demo类,即这个类默认是继承与超类的。
由于Any
这个类只是给咱们提供了equals()
、hashCode()
、toString()
这三个方法。咱们能够看看Any
这个类的源码实现:函数
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
多了几个方法与属性外,没有太大的区别。不过他们并非同一个类。这里就很少种讲解了....post
从上面源码中所产生的疑惑:类与函数前面都加上了open
这个修饰符。那么这个修饰符的做用是什么呢?
其实咱们分析能够得出:既然Any
类是全部类的父类,那么咱们本身要定义一个继承类,跟着Any
类的语法与结构就能定义一个继承类。故而,open
修饰符是咱们定义继承类的修饰符学习
- 定义继承类的关键字为:
open
。不论是类、仍是成员都须要使用open
关键字。定义格式为:this
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<String>) { println(DemoTest().num) DemoTest().foo() DemoTest().bar() }
输出结果为:
3 foo bar
分析:从上面的代码能够看出,DemoTest
类只是继承了Demo
类,并无实现任何的代码结构。同样可使用Demo
类中的属性与函数。这就是继承的好处。
在上一篇文章中,讲解到了
Kotlin
类,能够有一个主构造函数,或者多个辅助函数。或者没有构造函数的状况。若是您对Kotlin
的构造函数还不了解的状况,请阅读个人上一篇文章Kotlin——中级篇(一):类(class)详解
这里当实现类无主构造函数,和存在主构造函数的状况。
无主构造函数
当实现类无主构造函数时,则每一个辅助构造函数必须使用
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) }
能够看出,当实现类无主构造函数时,分别使用了super()
去实现了基类的三个构造函数。
存在主构造函数
当存在主构造函数时,主构造函数通常实现基类型中参数最多的构造函数,参数少的构造函数则用
this
关键字引用便可了。这点在Kotlin——中级篇(一):类(class)详解 这篇文章是讲解到的。
例:一样以自定义组件为例子
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) }
在
Kotlin
中关于函数的重载
与重写
,和Java
中是几乎是同样的,可是这里仍是举例来讲明一下。
2.3.一、重写函数中的两点特殊用法
不论是Java
仍是Kotlin
,重写基类型里面的方法,则称为重写,或者是覆盖基类型方法。不过这里介绍两点Kotlin
一点特殊的地方
- 当基类中的函数,没有用
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的
那么当一个基类去继承另一个基类时,第二个基类不想去覆盖掉第一个基类的方法时,第二个基类的该方法使用final
修饰符修饰。
例:
open class A{ open fun foo(){} } // B这个类继承类A,而且类B一样使用open修饰符修饰了的 open class B : Demo(){ // 这里使用final修饰符修饰该方法,禁止覆盖掉类A的foo()函数 final override fun foo(){} }
2.3.二、方法重载
在文章的开头提到了
多态
这个特性,方法的重载其实主要体如今这个地方。即函数名相同,函数的参数不一样的状况。这一点和Java
是相同的
这一点在继承类中一样有效:
例:
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<String>) { println(DemoTest().foo()) DemoTest().foo("foo的重载函数") }
输出结果为:
foo foo的重载函数
- 重写属性和重写方法其实大体是相同的,可是属性不能被重载。
- 重写属性即指:在基类中声明的属性,而后在其基类的实现类中重写该属性,该属性必须以
override
关键字修饰,而且其属性具备和基类中属性同样的类型。且能够重写该属性的值(Getter
)
例:
open class Demo{ open var num = 3 } class DemoTest : Demo(){ override var num: Int = 10 }
2.4.一、重写属性中,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<String>>){ println(DemoTest().valStr) val demo = DemoTest() demo.valStr = "1212121212" println(demo.valStr) }
输出结果为:
abc 1212121212
2.4.二、Getter()函数慎用super关键字
在这里值得注意的是,在实际的项目中在重写属性的时候不用get() = super.xxx
,由于这样的话,无论你是否从新为该属性赋了新值,仍是支持setter()
,在使用的时候都调用的是基类中的属性值。
例: 继上面中的例子
class DemoTest : Demo(){ /* * 这里介绍重写属性是,getter()函数中使用`super`关键字的状况 */ override var valStr: String = "abc"、 get() = super.valStr set(value){field = value} } fun main(arge: Array<String>>){ println(DemoTest().valStr) val demo = DemoTest() demo.valStr = "1212121212" println(demo.valStr) }
输出结果为:
我是用val修饰的属性 我是用val修饰的属性
切记:重写属性的时候慎用super
关键字。否则就是上面例子的效果
2.4.三、在主构造函数中重写
这一点和其实在
接口类
的文章中讲解过了,不清楚的能够去参见Kotlin——中级篇(五):枚举类(Enum)、接口类(Interface)详解。
例:基类仍是上面的例子
class DemoTest2(override var num: Int, override val valStr: String) : Demo() fun main(args: Array<String>){ val demo2 = DemoTest2(1,"构造函数中重写") println("num = ${demo2.num} \t valStr = ${demo2.valStr}") }
输出结果为:
num = 1 valStr = 构造函数中重写
这里的覆盖规则,是指实现类继承了一个基类,而且实现了一个接口类,当个人基类中的方法、属性和接口类中的函数重名的状况下,怎样去区分实现类到底实现哪个中的属性或属性。
这一点和一个类同时实现两个接口类,而两个接口都用一样的属性或者函数的时候是同样的。在接口类
这篇文章中已经讲解过,您能够参见Kotlin——中级篇(五):枚举类(Enum)、接口类(Interface)详解。
例:
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<A>.test1() super<B>.test1() } override fun test2() { super<A>.test2() super<B>.test2() } }
对于Kotlin
中继承类这一个知识点,在项目中用到的地方是很常见的。当你认真的学习完上面的内容,我相信你能够能很轻易的用于项目中,不过对一个类来讲,继承的代价较高,当实现一个功能没必要用到太多的集成属性的时候,能够用对象表达式
这一个高级功能去替代掉继承。
若是你有过其余面向对象语言的编程经验的话,你只要掌握其关键字、属性/函数重写、以及覆盖规则
这三个知识点就能够了。
若是各位大佬看了以后感受还阔以,就请各位大佬随便star
一下,您的关注是我最大的动力。
个人我的博客:Jetictors
Github:Jteictors
掘金:Jteictors