1.類的構造
1.1類的簡單定義
//java
public class MainActivity extends AppCompatActivity {
...
}
//Kotlin
class MainActivity : AppCompatActivity() {
...
}
Kotlin對類的寫法有以下特點:
<1>省略了關鍵字public,緣於它默認就是開放的;
<2>用冒號“:”代替extends,表示繼承關係;
<3>進行繼承時,父類後面多了括號()
class Animal {
//類的初始化函數init
init {
//Kotlin使用println代替Java的System.out.println
println("Animal:這是個動物類")
}
}
var animal = Animal()//類的實例創建,忽略new關鍵字
1.2 類的構造函數
//如果主構函數沒有帶@符號的註解說明,constructor就可以省略
//class AnimalMain (context:Context, name:String) {
class AnimalMain constructor(context:Context, name:String) {
init {
context.toast("這是隻$name")
}
}
//含二級構造函數
class AnimalMain constructor(context:Context, name:String) {
init {
context.toast("這是隻$name")
}
constructor(context:Context, name:String, sex:Int) : this(context,
name) {
var sexName:String = if(sex==0) "公" else "母"
context.toast("這隻${name}是${sexName}的")
}
}
<1>二級構造函數沒有函數名稱,只用關鍵字constructor表示這是一個構造函數;
<2>二級構造函數需要調用主構函數。
//構造函數調用
btn_class_main.setOnClickListener {
setAnimalInfo()
when (count%2) {
0 -> { var animal = AnimalMain(this, animalName) }
else -> { var animal = AnimalMain(this, animalName, animalSex) }
}
}
上述二級構造函數存在一個問題,會調用到主構函數的toast,爲了只調用對應的構造函數,改寫成如下形式:
class AnimalSeparate {
constructor(context:Context, name:String) {
context.toast("這是隻$name")
}
constructor(context: Context, name:String, sex:Int) {
var sexName:String = if(sex==0) "公" else "母"
context.toast("這隻${name}是${sexName}的")
}
}
1.3 帶默認參數的構造函數
class AnimalDefault (context: Context, name:String, sex:Int = 0) {
init {
var sexName:String = if(sex==0) "公" else "母"
context.toast("這隻${name}是${sexName}的")
}
}
var animal = AnimalDefault(this, animalName)//調用,默認sex爲“公”
注:上述類如果想要Java類識別默認參數,需要加入“@JvmOverloads”註解:
//因爲加入了註解標誌@,需要補上constructior關鍵字
class AnimalDefault @JvmOverloads constructor(context: Context, name:St
ring, sex:Int = 0) {
init {
var sexName:String = if(sex==0) "公" else "母"
context.toast("這隻${name}是${sexName}的")
}
}
AnimalDefault animal = new AnimalDefault(this, animalName);//java 代碼調用
2.類的成員
2.1成員屬性
class WildAnimal (var name:String, val sex) {
}
animal.name //相當於Java的get方法
成員變量需要var或val聲明,調用只需對象.成員變量即可。
2.2 成員方法
class WildAnimalFunction (var name:String, val sex:Int = 0) {
var sexName:String
init {
sexName = if(sex==0) "公" else "母"
}
//成員方法
fun getDesc(tag:String):String {
return "歡迎來到$tag:這隻${name}是${sexName}的"
}
}
animal.getDesc("動物園")//調用
2.3 伴生對象
class WildAnimalCompanion (var name:String, val sex:Int = 0) {
...
//在類加載時就運行伴生對象的代碼塊,其作用相當於Java裏面的static{...}代碼塊
//在關鍵字companion表示伴隨,object表示對象
companion object WildAnimal{
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> 0
"母","雌" -> 1
else -> -1
}
return sex
}
}
}
WildAnimalCompanion.WildAnimal.judgeSex(sexName)//調用
WildAnimalCompanion.judgeSex(sexName)//WildAnimal可省略
2.4 靜態屬性
companion object WildAnimal{
//靜態常量的值是不可變的,所以要使用關鍵字val修飾
val MALE = 0
val FEMALE = 1
val UNKNOWN = -1
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> MALE
"母","雌" -> FEMALE
else -> UNKNOWN
}
return sex
}
}
WildAnimalCompanion.MALE//調用
3 類的繼承
3.1開放性修飾詞
開放性修飾詞 | 說明 |
public | 對所有人開放。Kotlin的類、函數、變量不加開放性修飾詞的話,默認就是public類型 |
internal | 只對本模塊開放。這是Kotlin新增的關鍵字,對於app開發來說,本模塊便指app自身 |
protected | 只對自己和子類開放 |
privated | 只對自己開放,即私有 |
3.2類的繼承
Kotlin的類默認是不能繼承的(即final類型),如果需要繼承某類,該父類就應當聲明爲open類型。
open class Bird (var name:String, val sex:Int = MALE) {
//需要被繼承的方法也需要加上open
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 = 0
val FEMALE = 1
val UNKNOWN = -1
fun judgeSex(sexName:String):Int {
var sex:Int = when (sexName) {
"公","雄" -> MALE
"母","雌" -> FEMALE
else -> UNKNOWN
}
return sex
}
}
}
//繼承Bird的子類Duck
class Duck(name:String="鴨子", sex:Int = Bird.MALE) : Bird(name, sex) {
}
//activity調用
btn_class_duck.setOnClickListener {
var sexBird = if (count++%3==0) Bird.MALE else Bird.FEMALE
var duck = Duck(sex=sexBird)
tv_class_inherit.text = duck.getDesc("鳥語林")
}
繼承父類protected方法,標準寫法是“override protected”,然而protected可忽略。示例
class Ostrich(name:String="鴕鳥", sex:Int = Bird.MALE) : Bird(name, sex) {
//protected的方法繼承後可見性可升級爲public,但不能降級爲private
override public fun getSexName(sex:Int):String {
return if(sex==MALE) "雌" else "雄"
}
}
3.3 抽象類
abstract class Chicken(name:String, sex:Int, var voice:String) : Bird(n
ame, sex) {
...
abstract fun callOut(times:Int):String
}
子類的構造函數,原來的輸入參數不用加var或val,新增的輸入參數必須加入var或val,因爲抽象類不能直接使用,所以構造函數不必給默認參數賦值,抽象方法必須在子類重寫。
class Cock(name:String="雞", sex:Int = Bird.MALE, voice:String="喔喔喔") : Chicken(name, sex, voice) {
override fun callOut(times: Int): String {
var count = when {
times<=0 -> 0
times>=10 -> 9
else -> times
}
return "$sexName$name${voice}叫了${numberArray[count]}聲,原來它在報曉。"
}
}
tv_class_inherit.text = Cock().callOut(count++%10)//activity調用
3.4 接口
//接口不能帶構造函數
interface Behavior {
//接口內部的方法默認就是抽象的,open和abstract關鍵字可忽略
open abstract fun fly():String
fun swim():String
//kotlin接口與Java接口不一樣,允許實現方法
//該方法默認是open類型,接口的方法默認都是open類型
fun run():String {
return "大多數鳥兒跑得並不像樣"
}
//Kotlin的接口允許聲明抽象屬性,實現該接口的類必須重載該屬性
//與接口內部方法一樣,抽象屬性前面的open和abstract也可以忽略
var skilledSports:String
}
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所指的對象也可以是interface
return super.run()
}
//重載接口的抽象屬性
override var skilledSports:String = "游泳"
}
其他類實現接口時,跟類繼承一樣把接口名稱放在冒號後面,如果存在2個以上的接口或者既有父類也有接口,此時中間用逗號隔開。
btn_interface_behavior.setOnClickListener {
tv_class_inherit.text = when (count++%3) {
0 -> Goose().fly()
1 -> Goose().swim()
else -> Goose().run()
}
}
3.5 接口代理
一個類先聲明繼承自某個接口,但並不直接實現該接口的方法,而是把已經實現該接口的代理類作爲參數傳給前面的類,相當於告訴前面的類:“該接口的方法我已經代替你實現,你直接拿去用便是。” 這樣做的好處是,輸入參數可以按照具體的業務場景傳送相應的代理類。
示例:
//飛禽的行爲類
class BehaviorFly : Behavior {
override fun fly():String {
return "翱翔天空"
}
override fun swim():String {
return "落水鳳凰不如雞"
}
override fun run():String {
return "能飛幹嘛還要走"
}
override var skilledSports:String = "飛翔"
}
//水禽的行爲類
class BehaviorSwim : Behavior {
override fun fly():String {
return "看情況,大雁能展翅高飛,企鵝卻欲飛還休"
}
override fun swim():String {
return "怡然戲水"
}
override fun run():String {
return "趕鴨子上樹"
}
override var skilledSports:String = "游泳"
}
//走禽的行爲類
class BehaviorRun : Behavior {
override fun fly():String {
return "飛不起來"
}
override fun swim():String {
return "望洋興嘆"
}
override fun run():String {
return super.run()
}
override var skilledSports:String = "奔跑"
}
接着定義一個引用了代理類的野禽基類,通過關鍵字by表示該接口將由入參中的代理類實現。
//如果by的對象是個類,將編譯報錯
class WildFowl(name:String, sex:Int=MALE, behavior:Behavior) : Bird(name, sex), Behavior by behavior {
}
btn_delegate_behavior.setOnClickListener {
var fowl = when (count++%6) {
0 -> WildFowl("老鷹", Bird.MALE, BehaviorFly())
1 -> WildFowl("鳳凰", behavior=BehaviorFly())
2 -> WildFowl("大雁", Bird.FEMALE, BehaviorSwim())
3 -> WildFowl("企鵝", behavior=BehaviorSwim())
4 -> WildFowl("鴕鳥", Bird.MALE, BehaviorRun())
else -> WildFowl("燕子", behavior=BehaviorRun())
}
var action = when (count%11) {
in 0..3 -> fowl.fly()
4,7,10 -> fowl.swim()
else -> fowl.run()
}
tv_class_inherit.text = "${fowl.name}$action"
}
4 幾種特殊類
4.1嵌套類
在類的內部定義新類,這個新類叫作內部類。
class Tree(var treeName:String) {
class Flower (var flowerName:String) {
fun getName():String {
return "這是一朵$flowerName"
//普通的嵌套類不能訪問外部類的成員,如treeName
}
}
}
btn_class_nest.setOnClickListener {
val peachBlossom = Tree.Flower("桃花");
tv_class_secret.text = peachBlossom.getName()
}
4.2 內部類
嵌套類加上inner前綴,就成爲內部類
class Tree(var treeName:String) {
...
inner class Fruit (var fruitName:String) {
fun getName():String {
//只有聲明瞭內部類,才能訪問外部類的成員
return "這是${treeName}長出來的$fruitName"
}
}
}
4.3 枚舉類
將關鍵字enum 修飾class就變成枚舉類
enum class SeasonType {
SPRING, SUMMER, AUTUMN, WINTER
}
枚舉類屬性
ordinal屬性用於獲取該枚舉值的序號,name屬性用於獲取該枚舉值的名稱。
枚舉類如果存在構造函數,枚舉變量也必須調用對應的構造函數。
enum class SeasonName (val seasonName:String) {
SPRING("春天"),
SUMMER("夏天"),
AUTUMN("秋天"),
WINTER("冬天")
}
//調用
SeasonType.SPRING.ordinal
SeasonType.SPRING.name
SeasonName.SPRING.seasonName//調用構造函數的名稱
4.4 密封類
密封類像是一種更嚴格的枚舉類,它內部有且僅有的實例對象,所以是一個有限的自身實例集合。需要在該類的class前面添加sealed作爲標誌
sealed class SeasonSealed {
//密封類內部的每個嵌套類都必須繼承該類
class Spring (var name:String) : SeasonSealed()
class Summer (var name:String) : SeasonSealed()
class Autumn (var name:String) : SeasonSealed()
class Winter (var name:String) : SeasonSealed()
}
SeasonSealed.Spring("春天")
密封類確保條件分支覆蓋了所有的枚舉類型,因此不再需要else分支
tv_class_secret.text = when (season) {
is SeasonSealed.Spring -> season.name
is SeasonSealed.Summer -> season.name
is SeasonSealed.Autumn -> season.name
is SeasonSealed.Winter -> season.name
}
4.5 數據類
在class面前添加修飾詞data,並聲明擁有完整輸入參數的構造函數,那麼該類稱爲數據類。數據類有以下功能:
<1>自動聲明與構造函數入參同名的屬性字段;
<2>自動實現每個屬性字段的get/set方法;
<3>自動提供equals方法,用於比較兩個數據對象是否相等;
<4>自動提供copy()方法,允許完整複製某個數據對象,也可在複製後單獨修改某幾個字段的值;
<5>自動提供toString()方法,用於打印數據對象中保存的所有字段值
示例:
//數據類必須有主構函數,且至少有一個輸入參數
//輸入參數前要添加關鍵字var或val
//數據類不能是基類也不能是子類,不能是抽象類,也不是內部類,更不是密封類
data class Plant(var name:String, var stem:String, var leaf:String, var flower:String, var fruit:String, var seed:String) {
}
var lotus = Plant("蓮", "蓮藕", "蓮葉", "蓮花", "蓮蓬", "蓮子")
//數據類的copy()方法不帶參數,表示複製一模一樣的對象
var lotus2 = lotus.copy()
btn_class_data.setOnClickListener {
lotus2 = when (count++%2) {
0 -> lotus.copy(flower="")
else -> lotus.copy(flower="")
}
var result = if (lotus2.equals(lotus)) "相等" else "不等"
}
4.6 模板類
舉例說明
//在類名後面添加“<T>”,表示這是一個模板類
class River<T> (var name:String, var length:T) {
fun getInfo():String {
var unit:String = when (length) {
is String -> ""
//Int,Long,Float,Double都是數字類型Number
is Number -> "m"
else -> ""
}
return "${name}的長度是$length$unit。"
}
}
btn_class_generic.setOnClickListener {
var river = when (count++%4) {
//模板類(泛型類)聲明對象時,要在模板類的類名後面加上“<參數類型>”
0 -> River<Int>("小溪", 100)
//如果編譯器根據輸入參數就能知曉參數類型,也可直接忽略“<參數類型>”
1 -> River("瀑布", 99.9f)
....
}
...
}
備註:第1到第4篇kotlin基礎知識彙總全部摘自《Kotlin從零到精通(Android開發)》一書。