一迟几、內部類
在現實開發(fā)中消请,內部類出現的場景可以說非常多,例如类腮,adapter(適配器)中的viewHolder臊泰、數據模型中也有可能出現一個或者多個對象,這里的對象就是一個內部類蚜枢。讓我們來了解下內部類哩缸逃。
1.1.嵌套類
所謂的嵌套類,通俗來講就是一個類嵌套在另一個類里面
eg:
/**
* 嵌套類
*/
private var name = "biao"
fun outdoorMethou(){
//nesteMethou()//無法直接調用嵌套類的成員
//只能通過實例嵌套類來調用
val nestingClass = NestingClass()
nestingClass.nesteMethou()
}
class NestingClass{
fun nesteMethou(){
//println("name:$name")//無法使用外部類的成員
println("to do something magic")
}
}
fun main(args:Array<String>){
val nestingClass = OutdoorClass.NestingClass()
nestingClass.dowhat()
}
reslut:
to do something magic
說明:
- 嵌套類相當于Java中的靜態(tài)內部類厂抽,但是在kotlin中完全取消了static關鍵字需频,所以在kotlin類中,除了嵌套類筷凤,其余都是非靜態(tài)成員
- 在Java中靜態(tài)成員是不能訪問非靜態(tài)成員的贺辰,參考于此,可以解釋在嵌套類中不能訪問外部類的成員嵌施,但是可以訪問其他嵌套類
- 跟內部類一樣饲化,外部類不能直接調用嵌套類成員,如需調用吗伤,需創(chuàng)建嵌套類對象吃靠,通過對象調用嵌套類成員
- 調用嵌套類的屬性或方法的格式為:外部類.嵌套類().嵌套類方法/屬性。在調用的時候嵌套類是需要實例化的足淆。
1.2.內部類
內部類相當于Java中沒有用static修飾的內部類巢块,使用關鍵字:inner
eg:
class OutClass{
//外部類的私有屬性可以被內部類使用
private var name = "xie"
//外部類的私有函數可以被內部類使用
private fun outFun(){
println("i am outFun")
}
//通過此方法實例化內部類,再調用內部類方法
fun getInnerClassTodo(){
//innerFun()//這種外部類直接調用內部類成員的方式是編譯不通過的巧号,因為此時根本不存在內部類的對象
val innerClass = InnerClass()
//外部類想調用內部類族奢,需實例化內部類
innerClass.innerFun()
}
inner class InnerClass{
var age = 20
fun innerFun(){
println("name:$name+age:$age")
outFun()
}
}
}
fun main(args:Array<String>){
val innerClass = OutClass().InnerClass()
innerClass.innerFun()
}
reslut:
name:xie+age:20
i am outFun
說明:
- 外部類的成員可以被內部類使用,外部類無法直接使用內部類成員丹鸿,只能通過實例化內部類成員
- 調用內部類的屬性或方法的格式為:外部類().內部類().內部類方法/屬性越走。在調用的時候外部類、內部類都是需要實例化的。
1.3.匿名內部類
在實際開發(fā)中廊敌,匿名內部類并不陌生铜跑,隨處可見,像view的點擊事件:OnClickListener骡澈,多線程的實現锅纺。
其實匿名內部類就是一個沒有名字的內部類,通常用來簡化代碼肋殴,使用匿名內部類的前提條件是:必須繼承一個父類或者實現一個接口
eg:
/**
* 定義一個接口
*/
interface OnClickListener{
fun onItemClick(str : String)
}
/**
* 匿名內部類
*/
class AnonymousClass{
lateinit private var listener : OnClickListener
fun setOnClickListener(listener: OnClickListener){
this.listener = listener
}
fun testListener(){
listener.onItemClick("我是匿名內部類的測試方法")
}
}
fun main(args:Array<String>){
val anonymousClass = AnonymousClass()
anonymousClass.setOnClickListener(object :OnClickListener{
override fun onItemClick(str: String) {
println("to do something")
}
})
}
1.4.局部類
局部類:跟Java一樣囤锉,就是在方法中的類
eg:
class LocalClass{
var numOut = 1
fun partMouth(){
var name = "partMouth"
class PartClass{
var numPart : Int = 2
fun test(){
name = "PartClass"
numOut = 3
numPart = 4
println("我是局部類中的方法")
}
}
val partClass = PartClass()
println("name = $name \t numPart = " + partClass.numPart + "\t numOut = $numOut")
partClass.test()
println("name = $name \t numPart = " + partClass.numPart + "\t numOut = $numOut")
}
}
fun main(args:Array<String>){
val localClass = LocalClass()
localClass.partMouth()
}
result:
name = partMouth numPart = 2 numOut = 1
我是局部類中的方法
name = PartClass numPart = 4 numOut = 3
說明:
- 局部類只能在定義該局部類的方法中使用。
- 定義在實例方法中的局部類可以訪問外部類的所有變量和方法护锤。也能修改
- 局部類中的可以定義屬性官地、方法。并且可以修改局部方法中的變量蔽豺。
二区丑、抽象類
在我們日常開發(fā)中拧粪,一般會需要寫一個基類修陡,封裝一些方法以及處理一些共有的邏輯,只是不同的類會因其不同的功能實現不同的代碼可霎。就這樣的一個基類魄鸦,一般都是一個抽象類
2.1.抽象類的定義
抽象類:可以理解為一個模板,所有的子類根據這個模板填充自己的代碼
2.1.1 關鍵字
跟Java一樣癣朗,kotlin抽象類的關鍵字也是abstract
在這里我們需要注意的是抽象分為抽象類拾因、抽象函數和抽象屬性,而一個抽象類和一個普通類的區(qū)別就在于抽象類的除了可以有其自己的屬性旷余、構造函數和函數等組成部分绢记,還包含了抽象函數和抽象屬性。
eg:
abstract class AbstractClass {
//自身的屬性
var tag = this.javaClass.simpleName
//自身的方法
fun simpleFun():Unit{//Unit相當于java中的void正卧,表示沒有返回值蠢熄,可以省略不寫
TODO("do something")
}
//抽象屬性和方法
abstract var string:String
abstract fun init()
}
class ChildrenClass : AbstractClass() {
//不是抽象類的子類必須實現父類的方法和屬性
override var string: String
get() = tag
set(value) {}
override fun init() {
println("Implementing parent class methods,Obtain tag:$string")
}
}
abstract class AbsChildrenClass : AbstractClass(){
//抽象子類可不用實現父類的方法和屬性
var name = this.javaClass.simpleName
abstract fun check()
}
class absExtend : AbsChildrenClass(){
var className = this.javaClass.simpleName
override var string: String
get() = name
set(value) {}
override fun init() {
println("name:$name")
}
override fun check() {
if (className == string) println("do something")
else println("Nothing can be done")
}
}
fun main(args : Array<String>){
//抽象類不能直接實例化
//val abstractClass = AbstractClass()//Cannot create an instance of an abstract class
val childrenClass = ChildrenClass()
var abstractClass:AbstractClass = ChildrenClass()//若要實現抽象類的實例化炉旷,需要依靠子類采用向上轉型的方式處理
val absExtend = absExtend()
childrenClass.init()
absExtend.init()
absExtend.check()
}
result:
Implementing parent class methods签孔,Obtain tag:ChildrenClass
name:absExtend
do something
小結:
- 抽象類本身具有普通類特性,以及組成部分窘行。不過值得注意的是饥追,抽象類不能直接被實例化
- 抽象類是為其子類定義了一個模板。不同是類實現不同的功能
- 若普通類繼承了抽象類罐盔,需全部重寫父類的抽象方法和屬性但绕,如子類是抽象則不用
規(guī)則:
- 在Kotlin中的抽象類在頂層定義的時候只能使用public可見性修飾符修飾。
- 抽象類中可以定義內部抽象類惶看。
- 只能繼承一個抽象類壁熄。
- 若要實現抽象類的實例化帚豪,需要依靠子類采用向上轉型的方式處理。eg:var abstractClass:AbstractClass = ChildrenClass()
- 抽象類可以繼承自一個繼承類草丧,即抽象類可以作為子類狸臣。不過,抽象類建議不用open修飾符修飾昌执,因為可以覆寫抽象類的父類的函數烛亦。
三、接口類
3.1 聲明以及用法
3.1.1 聲明
關鍵字:interface
定義格式:interface 接口名{
}
3.1.2 用法
關鍵字:“:”懂拾,表示實現該接口煤禽,Java不一樣的是,Java使用implement關鍵字
綜合之前我之前寫過的文章岖赋,內容使用到:的次數表示檬果,這里講一下:有哪些代表含義:
a、用于屬性的定義
b唐断、用于表示實現接口
c选脊、用戶繼承
d、用于方法返回類型定義
eg:
interface InterfaceClass{
fun funOne()
}
class Demo:InterfaceClass{
override fun funOne() {
println("funOne are using")
}
}
fun main(args: Array<String>) {
val demo = Demo()
demo.funOne()
}
result:
funOne are using
3.2.接口中的方法使用
eg:
interface InterfaceClass {
fun funOne()
fun funTwo(num: Int)
fun funThree(num: Int): Int
/**
* 定義了一個有參無返回值的方法
* 因為有結構體故可以不用重寫
*/
fun funFour(): String {
return "I am funFour"
}
}
class Demo : InterfaceClass {
override fun funOne() {
println("funOne are using")
}
override fun funTwo(num: Int) {
println("funTwo are using and parem is num:$num")
}
override fun funThree(num: Int): Int {
println("funThree are using and parem is num:$num")
return num + 1
}
/**
* 接口中的funFour()方法默認返回”funFour“字符串.
* 可以用super.funFour()返回默認值
* 也可以不用super關鍵字脸甘,自己返回一個字符串
*/
override fun funFour(): String {
println("user I am funFour")
return super.funFour()
}
}
fun main(args: Array<String>) {
val demo = Demo()
demo.funOne()
demo.funTwo(8)
println(demo.funThree(9))
demo.funFour()
}
result:
funOne are using
funTwo are using and parem is num:8
funThree are using and parem is num:9
10
user I am funFour
I am funFour
說明:
- 不帶結構體的函數可以省略大括號恳啥,且不用強制重寫帶結構體的函數就可以直接調用。
- 接口方法可以默認實現丹诀,Java8中需要一個default關鍵字钝的,kotlin只需要寫方法體就好
3.3.接口中的屬性使用
- 在接口中申明屬性。接口中的屬性要么是抽象的铆遭,要么提供訪問器的實現硝桩。接口屬性不可以有后備字段。而且訪問器不可以引用它們枚荣。
3.3.1 作為抽象的
eg:
interface InterfaceFeild {
var num:Int
var name:String
}
class Demo1(override var num: Int, override var name: String) :InterfaceFeild{
fun test(){
println("num:$num\t+name:$name")
}
}
fun main(args: Array<String>) {
val demo1 = Demo1(21, "biao")
demo1.test()
}
result:
num:21 name:biao
說明:
- 即重寫屬性的時候是在實現類的類參數中碗脊。這也是用代碼提示去重寫的實現方法
3.3.2 作為訪問器
手動方式去重寫,并提供get方法
eg:
interface InterfaceFeild {
var num:Int
var name:String
val age:Int
get() = 2
var number:Int
}
class Demo1(override var num: Int, override var name: String) :InterfaceFeild{
fun test(){
println("age:$age\tnumber:$number")
}
override val age: Int
get() = super.age
override var number: Int = 25
}
fun main(args: Array<String>) {
val demo1 = Demo1(21, "biao")
demo1.test()
demo1.number = 3
demo1.test()
}
result:
num:21 name:biao
age:2 number:25
num:21 name:biao
age:2 number:3
3.3.3 接口的沖突問題解決
該問題是指當我們在父類中聲明了許多類型棍弄,有可能出現一個方法的多種實現
eg:
interface InfOne{
fun funTest(){
println("I am InfOne")
}
}
interface InfTwo{
fun funTest(){
println("I am InfTwo")
}
}
class Demo2 : InfOne,InfTwo{
override fun funTest() {
super<InfOne>.funTest()
super<InfTwo>.funTest()
}
}
fun main(args: Array<String>) {
val demo2 = Demo2()
demo2.funTest()
}
說明:Demo4實現了InfOne和InfTwo兩個接口望薄,而兩個接口中都存在兩個相同方法名的方法。因此編譯器不知道應該選哪個呼畸,故而我們用super<接口名>.方法名來區(qū)分痕支。
四、繼承類
4.1 超類 Any
在Kotlin中蛮原,說有的類都是繼承與Any類卧须,這是這個沒有父類型的類。即當我們定義各類時,它默認是繼承與Any這個超類的
來看看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多了幾個方法與屬性外侍芝,沒有太大的區(qū)別。
此外埋同,從上面的源碼中我們可以看到類和函數都有open關鍵字州叠,那么這個修飾符的作用是什么呢?
來凶赁,讓我們來分析一下咧栗,超類(Any)是所有類的父類,而分類中所有元素都有open修飾符虱肄,如果我們跟Any的語法去寫一個類致板,是不是就是一個繼承類了,說明浩峡,open修飾符是我們定義繼承類的修飾符
4.2 定義
4.2.1 基礎使用
- 定義繼承類的關鍵字為:open可岂。不管是類错敢、還是成員都需要使用open關鍵字翰灾。
- 定義格式為:
open class 類名{
open var/val 屬性名 = 屬性值
open fun 函數名()
}
eg:
open class InheritClass{
open var num = 3
open fun funOne() = "funOne"
open fun funTwo() = 25
}
class DemoTest : InheritClass(){
// 這里值得注意的是:Kotlin使用繼承是使用`:`符號,而Java是使用extends關鍵字
}
fun main(args:Array<String>){
val demoTest = DemoTest()
println(demoTest.num)
println(demoTest.funOne())
println(demoTest.funTwo())
}
result:
3
funOne
25
說明:
從上面的代碼可以看出稚茅,DemoTest類只是繼承了Demo類纸淮,并沒有實現任何的代碼結構。一樣可以使用Demo類中的屬性與函數亚享。這就是繼承的好處咽块。
4.2.2 構造函數
在上次的分享中有提到過,在kotlin中欺税,可以有一個主構造函數侈沪,或者多個輔助函數⊥碓洌或者沒有構造函數的情況亭罪。
在這里,來說一下實現類無主構造函數歼秽,和存在主構造函數的情況应役。
- 無主構造函數
當實現類無主構造函數時,則每個輔助構造函數必須使用super關鍵字初始化基類型,或者委托給另一個構造函數箩祥。 請注意院崇,在這種情況下,不同的輔助構造函數可以調用基類型的不同構造函數
這里用view的自定義來說明一下袍祖,eg:
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)詳解這篇文章是講解到的柑营。
這里同樣用view自定義的例子,eg:
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)
}
4.2.3 函數的重載和重寫
kotlin的重載和重寫的理解和Java一樣村视,重載:是在一個類里面官套,方法名字相同,而參數不同蚁孔。返回類型可以相同也可以不同奶赔;重寫是子類對父類的允許訪問的方法的實現過程進行重新編寫,返回值和形參都不能改變。即外殼不變杠氢,核心重寫站刑!
4.2.3.1 重寫函數中的兩點特殊用法
這里介紹兩點kotlin相較于Java特殊的地方
- 當基類中的函數,沒有用open修飾符修飾的時候鼻百,實現類中出現的函數的函數名不能與基類中沒有用open修飾符修飾的函數的函數名相同绞旅,不管實現類中的該函數有無override修飾符修飾。
eg:
open class InheritClass{
fun test(){}
}
class DemoTest : InheritClass(){
// 這里聲明一個和基類型無open修飾符修飾的函數温艇,且函數名一致的函數
// fun test(){} 編輯器直接報紅因悲,根本無法運行程序
// override fun test(){} 同樣報紅
}
- 當一個類不是用open修飾符修飾時,這個類默認是final的
4.2.3.1 方法重載
方法重載也是體現了面向對象中的多態(tài)的特性
方法重載在繼承類同樣有效
eg:
open class InheritClass{
open fun funThree() = "funThree"
}
class DemoTest : InheritClass(){
fun funThree(name:String):String{
return name
}
override fun funThree(): String {
return super.funThree()
}
}
fun main(args:Array<String>){
val demoTest = DemoTest()
println(demoTest.funThree())
demoTest.funThree("I am funThree,Do you known polymorphic?")
}
result:
funThree
I am funThree,Do you known polymorphic?
4.2.4 重寫屬性
- 重寫屬性和重寫方法其實大致是相同的勺爱,但是屬性不能被重載晃琳。
- 重寫屬性即指:在基類中聲明的屬性,然后在其基類的實現類中重寫該屬性琐鲁,該屬性必須以override關鍵字修飾卫旱,并且其屬性具有和基類中屬性一樣的類型。且可以重寫該屬性的值(Getter)
eg:
open class InheritClass{
open var num = 3
}
class DemoTest : InheritClass(){
override var num: Int = 10
}
fun main(args:Array<String>){
val demoTest = DemoTest()
println(demoTest.num)
}
result:
10
4.2.4.1 重寫屬性中围段,val與var的區(qū)別
有一點值得我們注意的是當基類中屬性的變量修飾符為val的使用顾翼,其實現類可以用重寫屬性可以用var去修飾。反之則不能蒜撮。
eg:
open class InheritClass {
open val valStrOne = "I am val string one"
open val valStrTwo = "I am val string two"
open val valStrThree = "I am val string three"
open val valStrFour = "I am val string four"
}
class DemoTest : InheritClass() {
override val valStrOne: String
get() = super.valStrOne
override var valStrTwo: String = ""
get() = super.valStrTwo
override val valStrThree: String = ""
override var valStrFour: String = "abc"
set(value){field = value}
}
fun main(args: Array<String>) {
val demoTest = DemoTest()
println(demoTest.valStrOne)
println(demoTest.valStrTwo)
println(demoTest.valStrThree)
println(demoTest.valStrFour)
demoTest.valStrFour = "I am Icon man"
println(demoTest.valStrFour)
}
result:
I am val string one
I am val string two
I am val string three
I am val string four
I am Icon man
4.2.4.2暴构、Getter()函數慎用super關鍵字
在這里值得注意的是跪呈,在實際的項目中在重寫屬性的時候不用get() = super.xxx,因為這樣的話,不管你是否重新為該屬性賦了新值取逾,還是支持setter(),在使用的時候都調用的是基類中的屬性值耗绿。
eg:
open class InheritClass {
open val valStrFive = "I am val string five"
}
class DemoTest : InheritClass() {
override var valStrFive: String = "cba"
get() = super.valStrFive
set(value){field = value}
}
fun main(args: Array<String>) {
val demoTest = DemoTest()
println(demoTest.valStrFive)
demoTest.valStrFive
println(demoTest.valStrFive)
}
result:
I am val string five
I am val string five