Kotlin入門(14)繼承的那些事兒

上一篇文章介紹了類對成員的聲明方式與使用過程,從而初步了解了類的成員及其運用。不過早在《Kotlin入門(12)類的概貌與構造》中,提到MainActivity繼承自AppCompatActivity,而Kotlin對于類繼承的寫法是“class MainActivity : AppCompatActivity() {}”,這跟Java對比有明顯差異,那么Kotlin究竟是如何定義基類并由基類派生出子類呢?為廓清這些迷霧,本篇文章就對類繼承的相關用法進行深入探討。

?

博文《Kotlin入門(13)類成員的眾生相》在演示類成員時多次重寫了WildAnimal類,這下你興沖沖地準備按照MainActivity的繼承方式,從WildAnimal派生出一個子類Tiger,寫好構造函數的兩個輸入參數,補上基類的完整聲明,敲了以下代碼不禁竊喜這么快就大功告成了:

class Tiger(name:String="老虎", sex:Int = 0) : WildAnimal(name, sex) {
}

誰料編譯器無情地蹦出錯誤提示“The type is final, so it cannot be inherited from”,意思是WildAnimal類是final類型,所以它不允許被繼承。原來Java默認每個類都能被繼承,除非加了關鍵字final表示終態,才不能被其它類繼承。Kotlin恰恰相反,它默認每個類都不能被繼承(相當于Java類被final修飾了),如果要讓某個類成為基類,則需把該類開放出來,也就是添加關鍵字open作為修飾。因此,接下來還是按照Kotlin的規矩辦事,重新寫個采取open修飾的基類,下面即以鳥類Bird進行演示,改寫后的基類代碼框架如下:

open class Bird (var name:String, val sex:Int = 0) {//此處暫時省略基類內部的成員屬性和方法
}

現在有了基類框架,還得往里面補充成員屬性和成員方法,然后給這些成員添加開放性修飾符。就像大家在Java和C++世界中熟知的幾個關鍵字,包括public、protected、private,分別表示公開、只對子類開放、私有。那么Kotlin體系參照Java世界也給出了四個開放性修飾符,按開放程度從高到低分別是:
public : 對所有人開放。Kotlin的類、函數、變量不加開放性修飾符的話,默認就是public類型。
internal : 只對本模塊內部開放,這是Kotlin新增的關鍵字。對于App開發來說,本模塊便是指App自身。
protected : 只對自己和子類開放。
private : 只對自己開放,即私有。
注意到這幾個修飾符與open一樣都加在類和函數前面,并且都包含“開放”的意思,乍看過去還真有點撲朔迷離,到底open跟四個開放性修飾符是什么關系?其實也不復雜,open不控制某個對象的訪問權限,只決定該對象能否繁衍開來,說白了,就是公告這個家伙有沒有資格生兒育女。只有頭戴open帽子的類,才允許作為基類派生出子類來;而頭戴open帽子的函數,表示它允許在子類中進行重寫。
至于那四個開放性修飾符,則是用來限定允許訪問某對象的外部范圍,通俗地說,就是哪里的男人可以娶這個美女。頭戴public的,表示全世界的男人都能娶她;頭戴internal的,表示本國的男人可以娶她;頭戴protected的,表示本單位以及下屬單位的男人可以娶她;頭戴private的,表示肥水不流外人田,只有本單位的帥哥才能娶這個美女噢。
因為private的限制太嚴厲了,只對自己開放,甚至都不允許子類染指,所以它跟關鍵字open勢同水火。open表示這個對象可以被繼承,或者可以被重載,然而private卻堅決斬斷該對象與其子類的任何關系,因此二者不能并存。倘若在代碼中強行給某個方法同時加上open和private,編譯器只能無奈地報錯“Modifier 'open' is incompatible with 'private'”,意思是open與private不兼容。
按照以上的開放性相關說明,接下來分別給Bird類的類名、函數名、變量名加上修飾符,改寫之后的基類代碼是下面這樣:

//Kotlin的類默認是不能繼承的(即final類型),如果需要繼承某類,則該父類應當聲明為open類型。
//否則編譯器會報錯“The type is final, so it cannot be inherited from”。
open class Bird (var name:String, val sex:Int = MALE) {//變量、方法、類默認都是public,所以一般都把public省略掉了//public var sexName:Stringvar sexName:Stringinit {sexName = getSexName(sex)}//私有的方法既不能被外部訪問,也不能被子類繼承,因此open與private不能共存//否則編譯器會報錯:Modifier 'open' is incompatible with 'private'//open private fun getSexName(sex:Int):String {open protected fun getSexName(sex:Int):String {return if(sex==MALE) "公" else "母"}fun getDesc(tag:String):String {return "歡迎來到$tag:這只${name}是${sexName}的。"}companion object BirdStatic{val MALE = 0val FEMALE = 1val UNKNOWN = -1fun judgeSex(sexName:String):Int {var sex:Int = when (sexName) {"公","雄" -> MALE"母","雌" -> FEMALEelse -> UNKNOWN}return sex}}
}

好不容易鼓搗出來一個正兒八經的鳥兒基類,再來聲明一個它的子類試試,例如鴨子是鳥類的一種,于是下面有了鴨子的類定義代碼:

//注意父類Bird已經在構造函數聲明了屬性,故而子類Duck無需重復聲明屬性
//也就是說,子類的構造函數,在輸入參數前面不要再加val和var
class Duck(name:String="鴨子", sex:Int = Bird.MALE) : Bird(name, sex) {
}

子類也可以定義新的成員屬性和成員方法,或者重寫被聲明為open的父類方法。比方說性別名稱“公”和“母”一般用于家禽,像公雞、母雞、公鴨、母鴨等等,指代野生鳥類的性別則通常使用“雄”和“雌”,所以定義野生鳥類的時候,就得重寫獲取性別名稱的getSexName方法,把“公”和“母”的返回值改為“雄”和“雌”。方法重寫之后,定義了鴕鳥的類代碼如下所示:

class Ostrich(name:String="鴕鳥", sex:Int = Bird.MALE) : Bird(name, sex) {//繼承protected的方法,標準寫法是“override protected”//override protected fun getSexName(sex:Int):String {//不過protected的方法繼承過來默認就是protected,所以也可直接省略protected//override fun getSexName(sex:Int):String {//protected的方法繼承之后允許將可見性升級為public,但不能降級為privateoverride public fun getSexName(sex:Int):String {return if(sex==MALE) "雄" else "雌"}
}

  

除了上面講的普通類繼承,Kotlin也存在與Java類似的抽象類,抽象類之所以存在,是因為其內部擁有被abstract修飾的抽象方法。抽象方法沒有具體的函數體,故而外部無法直接聲明抽象類的實例;只有在子類繼承之時重寫抽象方法,該子類方可正常聲明對象實例。舉個例子,雞屬于鳥類,可公雞和母雞的叫聲是不一樣的,公雞是“喔喔喔”地叫,而母雞是“咯咯咯”地叫;所以雞這個類的叫喚方法callOut,發出什么聲音并不確定,只能先聲明為抽象方法,連帶著雞類Chicken也變成抽象類了。根據上述的抽象類方案,定義好的Chicken類代碼示例如下:

//子類的構造函數,原來的輸入參數不用加var和val,新增的輸入參數必須加var或者val。
//因為抽象類不能直接使用,所以構造函數不必給默認參數賦值。
abstract class Chicken(name:String, sex:Int, var voice:String) : Bird(name, sex) {val numberArray:Array<String> = arrayOf("一","二","三","四","五","六","七","八","九","十");//抽象方法必須在子類進行重寫,所以可以省略關鍵字open,因為abstract方法默認就是open類型//open abstract fun callOut(times:Int):Stringabstract fun callOut(times:Int):String
}

接著從Chicken類派生出公雞類Cock,指定公雞的聲音為“喔喔喔”,同時還要重寫callOut方法,明確公雞的叫喚行為。具體的Cock類代碼如下所示:

class Cock(name:String="雞", sex:Int = Bird.MALE, voice:String="喔喔喔") : Chicken(name, sex, voice) {override fun callOut(times: Int): String {var count = when {//when語句判斷大于和小于時,要把完整的判斷條件寫到每個分支中times<=0 -> 0times>=10 -> 9else -> times}return "$sexName$name${voice}叫了${numberArray[count]}聲,原來它在報曉呀。"}
}

同樣派生而來的母雞類Hen,也需指定母雞的聲音“咯咯咯”,并重寫callOut叫喚方法,具體的Hen類代碼如下所示:

class Hen(name:String="雞", sex:Int = Bird.FEMALE, voice:String="咯咯咯") : Chicken(name, sex, voice) {override fun callOut(times: Int): String {var count = when {times<=0 -> 0times>=10 -> 9else -> times}return "$sexName$name${voice}叫了${numberArray[count]}聲,原來它下蛋了呀。"}
}

定義好了callOut方法,外部即可調用Cock類和Hen類的該方法了,調用代碼示例如下:

    //調用公雞類的叫喚方法tv_class_inherit.text = Cock().callOut(count++%10)//調用母雞類的叫喚方法tv_class_inherit.text = Hen().callOut(count++%10)

  

既然提到了抽象類,就不得不提接口interface。Kotlin的接口與Java一樣是為了間接實現多重繼承,由于直接繼承多個類可能存在方法沖突等問題,因此Kotlin在編譯階段就不允許某個類同時繼承多個基類,否則會報錯“Only one class may appear in a supertype list”。于是乎,通過接口定義幾個抽象方法,然后在實現該接口的具體類中重寫這幾個方法,從而間接實現C++多重繼承的功能。
在Kotlin中定義接口需要注意以下幾點:
1、接口不能定義構造函數,否則編譯器會報錯“An interface may not have a constructor”;
2、接口的內部方法通常要被實現它的類進行重寫,所以這些方法默認為抽象類型;
3、與Java不同的是,Kotlin允許在接口內部實現某個方法,而Java接口的所有內部方法都必須是抽象方法;
Android開發最常見的接口是控件的點擊監聽器View.OnClickListener,其內部定義了控件的點擊動作onClick,類似的還有長按監聽器View.OnLongClickListener、選擇監聽器CompoundButton.OnCheckedChangeListener等等,它們無一例外都定義了某種行為的事件處理過程。對于本文的鳥類例子而言,也可通過一個接口定義鳥兒的常見動作行為,譬如鳥兒除了叫喚動作,還有飛翔、游泳、奔跑等等動作,有的鳥類擅長飛翔(如大雁、老鷹),有的鳥類擅長游泳(如鴛鴦、鸕鶿),有的鳥類擅長奔跑(如鴕鳥、鴯鹋)。因此針對鳥類的飛翔、游泳、奔跑等動作,即可聲明Behavior接口,在該接口中定義幾個行為方法如fly、swim、run,下面是一個定義好的行為接口代碼例子:

//Kotlin與Java一樣不允許多重繼承,即不能同時繼承兩個類(及以上類),
//否則編譯器報錯“Only one class may appear in a supertype list”,
//所以仍然需要接口interface來間接實現多重繼承的功能。
//接口不能帶構造函數(那樣就變成一個類了),否則編譯器報錯“An interface may not have a constructor”
//interface Behavior(val action:String) {
interface Behavior {//接口內部的方法默認就是抽象的,所以不加abstract也可以,當然open也可以不加open abstract fun fly():String//比如下面這個swim方法就沒加關鍵字abstract,也無需在此處實現方法fun swim():String//Kotlin的接口與Java的區別在于,Kotlin接口內部允許實現方法,//此時該方法不是抽象方法,就不能加上abstract,//不過該方法依然是open類型,接口內部的所有方法都默認是open類型fun run():String {return "大多數鳥兒跑得并不像樣,只有鴕鳥、鴯鹋等少數鳥類才擅長奔跑。"}//Kotlin的接口允許聲明抽象屬性,實現該接口的類必須重載該屬性,//與接口內部方法一樣,抽象屬性前面的open和abstract也可省略掉//open abstract var skilledSports:Stringvar skilledSports:String
}

那么其他類實現Behavior接口時,跟類繼承一樣把接口名稱放在冒號后面,也就是說,Java的extends和implement這兩個關鍵字在Kotlin中都被冒號取代了。然后就像重寫抽象類的抽象方法一樣,重寫該接口的抽象方法,以鵝的Goose類為例,重寫接口方法之后的代碼如下所示:

class Goose(name:String="鵝", sex:Int = Bird.MALE) : Bird(name, sex), Behavior {override fun fly():String {return "鵝能飛一點點,但飛不高,也飛不遠。"}override fun swim():String {return "鵝,鵝,鵝,曲項向天歌。白毛浮綠水,紅掌撥清波。"}//因為接口已經實現了run方法,所以此處可以不用實現該方法,當然你要實現它也行。override fun run():String {//super用來調用父類的屬性或方法,由于Kotlin的接口允許實現方法,因此super所指的對象也可以是interfacereturn super.run()}//重載了來自接口的抽象屬性override var skilledSports:String = "游泳"
}

這下大功告成,Goose類聲明的群鵝不但具備鳥類的基本功能,而且能飛、能游、能跑,活脫脫一只栩栩如生的大白鵝呀:

    btn_interface_behavior.setOnClickListener {tv_class_inherit.text = when (count++%3) {0 -> Goose().fly()1 -> Goose().swim()else -> Goose().run()}}

  

總結一下,Kotlin的類繼承與Java相比有所不同,首先Kotlin的類默認不可被繼承,如需繼承則要添加open聲明;而Java的類默認是允許被繼承的,只有添加final聲明才表示不能被繼承。其次,Kotlin除了常規的三個開放性修飾符public、protected、private,另外增加了修飾符internal表示只對本模塊開放。再次,Java的類繼承關鍵字extends,以及接口實現關鍵字implement,在Kotlin中都被冒號所取代。最后,Kotlin允許在接口內部實現某個方法,而Java接口的內部方法只能是抽象方法。

__________________________________________________________________________
本文現已同步發布到微信公眾號“老歐說安卓”,打開微信掃一掃下面的二維碼,或者直接搜索公眾號“老歐說安卓”添加關注,更快更方便地閱讀技術干貨。

?

轉載于:https://www.cnblogs.com/aqi00/p/7380070.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/540737.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/540737.shtml
英文地址,請注明出處:http://en.pswp.cn/news/540737.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在Scala中列出| 關于Scala列表的完整教程

Scala | 清單 (Scala | List) List in Scala is a collection that stores data in the form of a liked-list. The list is an immutable collection which means the elements of a list cannot be altered after the list is created. Lists are flexible with data types, …

MySQL單機多實例部署詳解之------多實例分別定義不同的配置文件

mysql多實例安裝---分別定義不同的配置文件1.安裝MySQL需要的依賴包[rootMySQL ~]# yum install ncurses-devel libaio-devel cmake -y[rootMySQL ~]# rpm -qa ncurses-devel libaio-develncurses-devel-5.7-4.20090207.el6.x86_64libaio-devel-0.3.107-10.el6.x86_642.安裝編譯…

javascript模塊_JavaScript中的模塊

javascript模塊JavaScript模塊 (JavaScript Modules) One of the key features of programming fundamentals is breaking down your code into fragments. These fragments depending on its functionality have been coined various terms like functions, components, modul…

在mac上安裝Docker

1.進入一下地址進行下載docker https://download.docker.com/mac/stable/Docker.dmg 進入后進行下載后進行安裝 2.將其拖動到Appliaction中即可 3.第一打開會有一個這樣的歡迎頁面 3.檢查是否安裝完成 出現上圖所示標示安裝完成了

composer 檢查鏡像_檢查N元樹中的鏡像

composer 檢查鏡像Problem statement: 問題陳述&#xff1a; Given two n-ary trees, the task is to check if they are mirrors of each other or not. 給定兩個n元樹&#xff0c;任務是檢查它們是否互為鏡像。 Note: you may assume that root of both the given tree as …

浪潮各機型前面板指示燈含義

NF560D2 NF3020M2 NF5020M3 NF5140M3 NF5212H2 NF5220 NF5224L2 NF5240M3 NF5270M3 NF5280M2 NF5280M3 NF5540M3 NF5580M3 NF8420M3 NF8520 NF8560M2 說明&#xff1a;轉浪潮官網。

python dll 混合_Python | 條線混合圖

python dll 混合In some of the cases, we need to plot a bar-line hybrid plot. This plot helps in a better understanding of dynamics as well as the relative magnitude of each point in the plot. Bar-Line Hybrid Plots are mostly used for the representation of …

測試八 賽后感受

測試八 當我打開T1的時候&#xff0c;就沒有往下看題目了&#xff0c;主要是發現T1就是之前做過&#xff0c;而且我也看過題解的題目&#xff0c;接著就開始鉆研&#xff0c;當然&#xff0c;也沒什么好鉆研的&#xff0c;大概思路還是知道的&#xff0c;再寫寫數據就已經很清晰…

推薦五個免費的網絡安全工具

導讀&#xff1a; 在一個完美的世界里&#xff0c;信息安全從業人員有無限的安全預算去做排除故障和修復安全漏洞的工作。但是&#xff0c;正如你將要學到的那樣&#xff0c;你不需要無限的預算取得到高質量的產品。這里有SearchSecurity.com網站專家Michael Cobb推薦的五個免費…

bios部署模式審核模式_BIOS的完整形式是什么?

bios部署模式審核模式BIOS&#xff1a;基本輸入輸出系統 (BIOS: Basic Input Output System) BIOS is an abbreviation of the Basic Input Output System. In the beginning, when you first set on your computer, the first software which starts run by the computer is &…

day04-裝飾器

一、裝飾器定義 1&#xff09;裝飾器&#xff1a;本質是函數。 2&#xff09;功能&#xff1a;用來裝飾其他函數&#xff0c;顧名思義就是&#xff0c;為其他的函數添加附件功能的。 二、原則 1&#xff09;不能修改被裝飾函數的源代碼 2&#xff09;不能修改被裝飾函數的調用方…

c 語言bool 類型數據_C ++中的bool數據類型

c 語言bool 類型數據In C programming language, to deal with the Boolean values – C added the feature of the bool data type. A bool variable stores either true (1) or false (0) values. 在C 編程語言中&#xff0c;為了處理布爾值– C 添加了bool數據類型的功能 。…

C ++中的std :: binary_search()

binary_search()作為STL函數 (binary_search() as a STL function) Syntax: 句法&#xff1a; bool binary_search (ForwardIterator first, ForwardIterator last, const T& value);Where, 哪里&#xff0c; ForwardIterator first iterator to start of the range For…

HNUSTOJ-1437 無題

1437: 無題 時間限制: 1 Sec 內存限制: 128 MB提交: 268 解決: 45[提交][狀態][討論版]題目描述 tc在玩一個很無聊的游戲&#xff1a;每一次電腦都會給一個長度不超過10^5的字符串&#xff0c;tc每次都從第一個字符開始&#xff0c;如果找到兩個相鄰相一樣的字符&#xff0c;…

凱撒密碼pythin密碼_凱撒密碼術

凱撒密碼pythin密碼Caesar cipher is one of the well-known techniques used for encrypting the data. Although not widely used due to its simplicity and being more prone to be cracked by any outsider, still this cipher holds much value as it is amongst the fir…

MultiQC使用指導

MultiQC使用指導 官網資料文獻&#xff1a;MultiQC --- summarize analysis results for multiple tools and samples in a single report參考資料一&#xff1a; 整合 fastq 質控結果的工具 簡介 MultiQC 是一個基于Python的模塊, 用于整合其它軟件的報告結果, 目前支持以下軟…

FYFG的完整形式是什么?

FYFG&#xff1a;對您的未來指導 (FYFG: For Your Future Guidance) FYFG is an abbreviation of "For Your Future Guidance". FYFG是“ For your Future Guidance”的縮寫 。 It is an expression, which is commonly used in the Gmail platform. It is also wr…

WorkerMan 入門學習之(二)基礎教程-Connection類的使用

一、TcpConnection類 的使用 1、簡單的TCP測試 Server.php <?php require_once __DIR__./Workerman/Autoloader.php; use Workerman\Worker; $worker new Worker(websocket://0.0.0.0:80);// 連接回調 $worker->onConnect function ($connection){echo "connecti…

kotlin獲取屬性_Kotlin程序獲取系統名稱

kotlin獲取屬性The task is to get the system name. 任務是獲取系統名稱。 package com.includehelpimport java.net.InetAddress/*** Function for System Name*/fun getSystemName(): String? {return try {InetAddress.getLocalHost().hostName} catch (E: Exception) {S…

71文件類型

1.kit類型 標準的SeaJs模塊文件類型&#xff0c;直接對外暴露方法。 2.units類型 依賴pageJob&#xff0c;對外暴露一個名字&#xff0c;pageJob依賴暴露的名字對模塊進行初始化&#xff0c;在pageJob內部邏輯自動執行init方法&#xff1b; 由于沒有對外暴露方法&#xff0c;只…