類和對(duì)象
/* -------------- 類和對(duì)象 -------------- */
/**
Kotlin 類可以包含:構(gòu)造函數(shù)和初始化代碼塊、函數(shù)倚评、屬性城瞎、內(nèi)部類埂蕊、對(duì)象聲明滞谢。
class Runoob { // 類名為 Runoob
// 大括號(hào)內(nèi)是類體構(gòu)成
}
聲明空類
class Empty
成員函數(shù)
class Runoob {
fun foo(){}
}
類的屬性
class Runoob {
var name:String
var url:String
var city:String
}
實(shí)例的創(chuàng)建
val site = Runoob()
屬性的引用
site.name="zzz"
site.url="action.call"
構(gòu)造方法
class Person constructor(firstName:String){}
如果主構(gòu)造器沒有任何注解髓迎,也沒有任何可見度修飾符硅瞧,那么constructor關(guān)鍵字可以省略偏瓤。
class Person(firstName:String){}
getter 和 setter
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
getter 和 setter 都是可選
如果屬性類型可以從初始化語句或者類的成員函數(shù)中推斷出來杀怠,那就可以省去類型,val不允許設(shè)置setter函數(shù)厅克,因?yàn)樗侵蛔x的赔退。
var allByDefault: Int? // 錯(cuò)誤: 需要一個(gè)初始化語句, 默認(rèn)實(shí)現(xiàn)了 getter 和 setter 方法
var initialized = 1 // 類型為 Int, 默認(rèn)實(shí)現(xiàn)了 getter 和 setter
val simple: Int? // 類型為 Int ,默認(rèn)實(shí)現(xiàn) getter 证舟,但必須在構(gòu)造函數(shù)中初始化
val inferredType = 1 // 類型為 Int 類型,默認(rèn)實(shí)現(xiàn) getter
*/
class Person {
var lastName: String = "zhang"
get() = field.toUpperCase()
set
/*Kotlin 中類不能有字段硕旗。提供了 Backing Fields(后端變量) 機(jī)制,備用字段使用field關(guān)鍵字聲明,field 關(guān)鍵詞只能用于屬性的訪問器*/
var no: Int = 100
get() = field
set(value) {
if (value < 10) {
field = value
} else {
field = -1
}
}
var height: Float = 145.4f
private set
}
fun t_obj_person() {
val person = Person()
person.lastName = "ratel"
println("lastName = ${person.lastName}")
person.no = 9
println("no = ${person.no}")
person.no = 21
println("no = ${person.no}")
}
public class MyTest {
// 非空屬性必須在定義的時(shí)候初始化,kotlin提供了一種可以延遲初始化的方案,使用 lateinit 關(guān)鍵字描述屬性
lateinit var subject: TestSubject
@SetUp
fun setup() {
subject = TestSubject()
}
@Test
fun test() {
subject.method()
}
}
annotation class SetUp
annotation class Test
class TestSubject {
fun method() {
}
}
/**主構(gòu)造器
* 主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中褪储,初始化代碼使用init關(guān)鍵字
*/
class BookBean constructor(name: String) {
init {
println("book name $name")
}
}
/**
* 注意:主構(gòu)造器的參數(shù)可以在初始化代碼段中使用卵渴,也可以在類主體n定義的屬性初始化代碼中使用慧域。 一種簡(jiǎn)潔語法鲤竹,可以通過主構(gòu)造器來定義屬性并初始化屬性值(可以是var或val):
* val 定義的常量是不可以修改的
* 如果構(gòu)造器有注解,或者有可見度修飾符,這時(shí)constructor關(guān)鍵字是必須的辛藻,注解和修飾符要放在它之前碘橘。
*/
class People(val firstName: String, val lastName: String) {
init {
println("firstName $firstName , lastName $lastName")
}
}
fun t_consutrctor() {
BookBean("三國(guó)")
People("kagle", "ai")
}
class Runoob constructor(name: String) {
var url = "http://www.runoob.com"
var contry: String = "CN"
var siteName = name
init {
println("初始化網(wǎng)站名: $name")
}
fun printTest() {
println("我是類的函數(shù)")
}
}
fun t_runoob() {
val runoob = Runoob("菜鳥教程")
println(runoob.siteName)
println(runoob.url)
println(runoob.contry)
runoob.printTest()
}
/**
* 次構(gòu)造函數(shù)
類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor:
*/
class NewPerson {
private val children: Children = Children()
constructor(parent: NewPerson) {
parent.children.add(this)
}
}
/**
* 如果類有主構(gòu)造函數(shù)吱肌,每個(gè)次構(gòu)造函數(shù)都要痘拆,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)。
* 在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字:
*/
class NewPerson2 constructor(val name: String) {
var age: Int = 0
init {
println("NewPerson2 init name : $name")
}
constructor(name: String, _age: Int) : this(name) {
age = _age
// 初始化
println("NewPerson2 次構(gòu)造 : name : $name , age : $age")
}
}
fun t_new_person2() {
val newPerson2 = NewPerson2("zzg", 32)
println("user name ${newPerson2.name} , age ${newPerson2.age}")
}
class Children {
var pList = ArrayList<NewPerson>()
fun add(person: NewPerson) {
if (!pList.contains(person)) {
pList.add(person)
}
}
}
/**
*如果一個(gè)非抽象類沒有聲明構(gòu)造函數(shù)(主構(gòu)造函數(shù)或次構(gòu)造函數(shù))氮墨,它會(huì)產(chǎn)生一個(gè)沒有參數(shù)的構(gòu)造函數(shù)纺蛆。構(gòu)造函數(shù)是 public 。如果你不想你的類有
* 公共的構(gòu)造函數(shù)规揪,你就得聲明一個(gè)空的主構(gòu)造函數(shù)
*/
class DontCreateMe private constructor() {
}
/**
* 注意:在 JVM 虛擬機(jī)中桥氏,如果主構(gòu)造函數(shù)的所有參數(shù)都有默認(rèn)值,編譯器會(huì)生成一個(gè)附加的無參的構(gòu)造函數(shù)猛铅,這個(gè)構(gòu)造函數(shù)會(huì)直接使用默認(rèn)值字支。這使得 Kotlin 可以更簡(jiǎn)單的使用像 Jackson 或者 JPA 這樣使用無參構(gòu)造函數(shù)來創(chuàng)建類實(shí)例的庫(kù)。
class Customer(val customerName: String = "")
*/
/* -------------- 類和對(duì)象 -------------- */
/* -------------- 抽象類 -------------- */
/**
* 抽象是面向?qū)ο缶幊痰奶卣髦患楹觯惐旧矶槲保蝾愔械牟糠殖蓡T,都可以聲明為abstract的栗菜。抽象成員在類中不存在具體的實(shí)現(xiàn)欠雌。
注意:無需對(duì)抽象類或抽象成員標(biāo)注open注解。
*/
open class BaseHandler {
open fun f() {}
}
// 抽象類
abstract class BaseDefaultHanlder : BaseHandler() {
// 抽象方法
override abstract fun f()
}
/* -------------- 抽象類 -------------- */
/* -------------- 嵌套類(靜態(tài)內(nèi)部類) -------------- */
class Outer {
private val bar: Int = 1
class Nested {
var ot: Outer = Outer()
fun test() {
// 嵌套類可以引用外部類私有變量疙筹,但要先創(chuàng)建外部類的實(shí)例桨昙,不能直接引用
println(ot.bar)
}
fun foo() = 2
}
}
// 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
fun t_nest_cls() {
// 嵌套類,Outter后邊沒有括號(hào)
val foo = Outer.Nested().foo()
println("foo = $foo")
}
/* -------------- 嵌套類(靜態(tài)內(nèi)部類) -------------- */
/* -------------- 內(nèi)部類 -------------- */
/**
* 內(nèi)部類使用 inner 關(guān)鍵字來表示腌歉。
內(nèi)部類會(huì)帶有一個(gè)對(duì)外部類的對(duì)象的引用蛙酪,所以內(nèi)部類可以訪問外部類成員屬性和成員函數(shù)。
*/
class Outer2 {
private val bar: Int = 2
var v = "成員屬性"
/*嵌套內(nèi)部類*/
inner class Inner2 {
// 訪問外部類成員
fun foo() = bar
fun innterTest() {
// 獲取外部類的成員變量
// 相當(dāng)于Outer.this
/*
為了消除歧義翘盖,要訪問來自外部作用域的 this桂塞,我們使用this@label,
其中 @label 是一個(gè) 代指 this 來源的標(biāo)簽馍驯。
*/
var o = this@Outer2
println("Outer2 內(nèi)部類可以引用外部類的成員: v " + o.v)
}
}
}
fun t_inner_cls() {
// 內(nèi)部類阁危,Outter后邊有括號(hào)
//
val foo = Outer2().Inner2().foo()
println("t_inner_cls foo = $foo")
val d = Outer2().Inner2().innterTest()
println("t_inner_cls d = $d")
}
/* -------------- 內(nèi)部類 -------------- */
/* -------------- 匿名內(nèi)部類 -------------- */
/*使用對(duì)象表達(dá)式來創(chuàng)建匿名內(nèi)部類*/
class Test2 {
var v = "成員屬性"
fun setInterFace(test: ITest) {
println("Test2 setInterFace v = $v ")
test.test()
}
}
// 定義接口
interface ITest {
fun test()
}
fun t_anonymous() {
val test2 = Test2()
/**
* 采用對(duì)象表達(dá)式來創(chuàng)建接口對(duì)象,即匿名內(nèi)部類的實(shí)例
*/
test2.setInterFace(object : ITest {
override fun test() {
println("接口回調(diào),對(duì)象表達(dá)式創(chuàng)建匿名內(nèi)部類的實(shí)例")
}
})
}
/* -------------- 匿名內(nèi)部類 -------------- */
/* -------------- 類的修飾符 -------------- */
/**
* 類的修飾符包括 classModifier 和_accessModifier_:
* classModifier: 類屬性修飾符汰瘫,標(biāo)示類本身特性狂打。
abstract // 抽象類
final // 類不可繼承,默認(rèn)屬性
enum // 枚舉類
open // 類可繼承混弥,類默認(rèn)是final的
annotation // 注解類
accessModifier: 訪問權(quán)限修飾符
private // 僅在同一個(gè)文件中可見
protected // 同一個(gè)文件中或子類可見
public // 所有調(diào)用的地方都可見
internal // 同一個(gè)模塊中可見
*/
private fun foo() {} // 在包類可見趴乡,包級(jí)函數(shù)
public var bar: Int = 5 // 該屬性隨處可見
internal val baz = 6 // 相同模塊內(nèi)可見
/* -------------- 類的修飾符 -------------- */
fun main(args: Array<String>) {
t_obj_person()
t_consutrctor()
t_runoob()
t_new_person2()
t_nest_cls()
t_inner_cls()
t_anonymous()
}
繼承
/* -------------- 繼承 -------------- */
/**
kotlin中所有的類都繼承Any,它是所有類的超父,對(duì)于沒有超類型聲明的類默認(rèn)繼承超類
class Exemaple // 從Any隱式繼承
Any默認(rèn)提供三個(gè)函數(shù):equals,hashCode,toString
Any不是java.lang.Object
如果一個(gè)類要被繼承,可以使用open關(guān)鍵字進(jìn)行修飾
*/
open class Base(p: Int) // 定義基類
class Dervied(p: Int) : Base(p)
/* -------------- 繼承 -------------- */
/* -------------- 構(gòu)造函數(shù) -------------- */
/**
*子類有主構(gòu)造函數(shù)
如果子類有主構(gòu)造函數(shù)晾捏, 則基類必須在主構(gòu)造函數(shù)中立即初始化
*/
open class New_EPerson(var name: String, var age: Int) { // 基類
}
class Student(name: String, age: Int, var no: String, var score: Int) : New_EPerson(name, age) {
}
fun t_ext_obj() {
val student = Student("runnob", 18, "S0035", 89)
println("姓名: ${student.name}")
println("年齡: ${student.age}")
println("學(xué)生號(hào): ${student.no}")
println("學(xué)分: ${student.score}")
}
/**
*子類沒有主構(gòu)造函數(shù)
如果子類沒有主構(gòu)造函數(shù)蒿涎,則必須在每一個(gè)二級(jí)構(gòu)造函數(shù)中用 super 關(guān)鍵字初始化基類,
或者在代理另一個(gè)構(gòu)造函數(shù)惦辛。初始化基類時(shí)劳秋,可以調(diào)用基類的不同構(gòu)造方法。
*/
open class Animal constructor(ctx: Context) {
// 次構(gòu)造
constructor(ctx: Context, attrs: AttributeSet) : this(ctx)
}
class Cat : Animal {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
//實(shí)例
open class User(name: String) {
// 次級(jí)構(gòu)造函數(shù)
constructor(name: String, age: Int) : this(name) {
println("---------- 基類次級(jí)構(gòu)造函數(shù) ----------")
}
}
class Worker : User {
constructor(name: String, age: Int, no: String, score: Int) : super(name, age) {
println("---------- 繼承類次級(jí)構(gòu)造函數(shù) ----------")
println("Worker 姓名: $name")
println("Worker 年齡: $age")
println("Worker 學(xué)生號(hào): $no")
println("Worker 學(xué)分: $score")
}
}
fun t_ext_user() {
Worker("test01", 23, "S1174", 135)
}
/**
*重寫
在基類中胖齐,使用fun聲明函數(shù)時(shí)玻淑,此函數(shù)默認(rèn)為final修飾,不能被子類重寫呀伙。如果允許子類重寫該函數(shù)岁忘,
那么就要手動(dòng)添加 open 修飾它, 子類重寫方法使用 override 關(guān)鍵詞
*/
// 用戶基類
open class NewUser {
open fun study() { // 允許子類重寫
println("我畢業(yè)了")
}
}
/*子類繼承NewUser類*/
class NewStudent : NewUser() {
override fun study() { // 重寫方法
println("我在讀大學(xué)")
}
}
fun t_ode() {
val student = NewStudent()
student.study()
}
// 如果有多個(gè)相同的方法(繼承或者實(shí)現(xiàn)自其他類,如A区匠、B類)干像,則必須要重寫該方法,使用super范型去選擇性地調(diào)用父類的實(shí)現(xiàn)驰弄。
open class A {
open fun f() {
println("A")
}
fun a() {
println("a")
}
}
interface B {
// //接口的成員變量默認(rèn)是 open 的
fun f() {
println("B")
}
fun b() {
println("b")
}
}
class C() : A(), B {
override fun f() {
println("C run f ... ")
// 選擇性的去調(diào)用父類的實(shí)現(xiàn)
super<A>.f()
super<B>.f()
}
}
fun t_oc() {
val c = C()
c.f()
}
/**
* 屬性重寫
屬性重寫使用 override 關(guān)鍵字麻汰,屬性必須具有兼容類型,每一個(gè)聲明的屬性都可以通過初始化程序或者getter方法被重寫:
*/
open class Foo {
open val x: Int
get() = 10
}
class Bar1 : Foo() {
override val x: Int
get() = super.x
}
/**
*
你可以用一個(gè)var屬性重寫一個(gè)val屬性戚篙,但是反過來不行五鲫。因?yàn)関al屬性本身定義了getter方法卤橄,重寫為var屬性會(huì)在衍生類中額外聲明一個(gè)setter方法
你可以在主構(gòu)造函數(shù)中使用 override 關(guān)鍵字作為屬性聲明的一部分:
*/
interface IFoo {
val count: Int
}
class Bar_1(override val count: Int) : IFoo
class Bar_2 : IFoo {
override var count: Int = 0
}
/* -------------- 構(gòu)造函數(shù) -------------- */
fun main(args: Array<String>) {
t_ext_obj()
t_ext_user()
t_ode()
t_oc()
}
接口
/* -------------- 接口 -------------- */
//接口與java8類似寝受,使用interface定義撩荣,允許方法有默認(rèn)實(shí)現(xiàn)
interface IMe {
fun bar()
fun foo() {
// 默認(rèn)實(shí)現(xiàn)
println("foo")
}
}
/**
* 實(shí)現(xiàn)接口
* 一個(gè)類或者對(duì)象可以實(shí)現(xiàn)一個(gè)或多個(gè)接口
*/
class MyChild : IMe {
override fun bar() {
println("bar")
}
}
fun t_mychild() {
val myChild = MyChild()
myChild.bar()
myChild.foo()
}
/**
* 接口中的屬性
* 接口中的屬性只能是抽象的吠裆,不允許初始化值,接口不會(huì)保存屬性值孵延,實(shí)現(xiàn)接口時(shí)贷腕,必須重寫屬性
*/
interface INewMe {
// 抽象屬性
var name: String
}
class NewMeImpl : INewMe {
// 重寫屬性
override var name: String = "runoob"
}
fun t_new_me() {
val meImpl = NewMeImpl()
println("name = ${meImpl.name}")
}
/**
* 函數(shù)的重寫
* 實(shí)現(xiàn)多個(gè)接口時(shí)惹苗,可能會(huì)遇到同一方法繼承多個(gè)實(shí)現(xiàn)的問題
*/
interface A_1 {
fun foo() {
println("A_1")
}
fun bar()
}
interface B_1 {
fun foo() {
println("B_1")
}
fun bar() {
println("B_1 bar")
}
}
class C_1 : A_1 {
override fun bar() {
println("C_1 bar")
}
}
class D_1 : A_1, B_1 {
override fun bar() {
println("D_1 bar")
super<B_1>.bar()
}
override fun foo() {
println("D_1 foo")
super<A_1>.foo()
super<B_1>.foo()
}
}
fun t_o_method() {
val d_1 = D_1()
d_1.foo()
d_1.bar()
}
/**
* 實(shí)例中接口 A 和 B 都定義了方法 foo() 和 bar()痛倚, 兩者都實(shí)現(xiàn)了 foo(), B 實(shí)現(xiàn)了 bar()规婆。因?yàn)?C 是一個(gè)實(shí)現(xiàn)了 A 的具體類,
* 所以必須要重寫 bar() 并實(shí)現(xiàn)這個(gè)抽象方法蝉稳。
然而抒蚜,如果我們從 A 和 B 派生 D,我們需要實(shí)現(xiàn)多個(gè)接口繼承的所有方法耘戚,并指明 D 應(yīng)該如何實(shí)現(xiàn)它們嗡髓。
這一規(guī)則 既適用于繼承單個(gè)實(shí)現(xiàn)(bar())的方法也適用于繼承多個(gè)實(shí)現(xiàn)(foo())的方法。
*/
/* -------------- 接口 -------------- */
fun main(args: Array<String>) {
t_mychild()
t_new_me()
t_o_method()
}
擴(kuò)展
package com.example.ext
/* -------------- 擴(kuò)展 -------------- */
/**
* kotlin可以對(duì)一個(gè)類的屬性和方法進(jìn)行擴(kuò)展收津,且不需要使繼承或使用Decorator模式
* 擴(kuò)展是一種靜態(tài)行為饿这,對(duì)于被擴(kuò)展的類代碼本身不會(huì)造成任何影響
*/
/**
* 擴(kuò)展函數(shù)
* 擴(kuò)展函數(shù)可以在已有的類中添加新的方法浊伙,不會(huì)對(duì)原類做修改
* fun receiverType.functionName(params){
* body
* }
*
* receiverType 要擴(kuò)展的類
* functionName 方法名稱
*/
class New_User(var name: String)
// 擴(kuò)展函數(shù)
fun New_User.Print() {
println("User .. $name")
}
fun t_user_print() {
val user = New_User("zz")
user.Print()
}
/**
* 為MutableList添加一個(gè)swap函數(shù),交換不同位置的值
*/
fun MutableList<Int>.swap(_i: Int, _j: Int) {
var tmp = this[_i]
this[_i] = this[_j]
this[_j] = tmp
// this關(guān)鍵字指代接收者對(duì)象(receiver object)(也就是調(diào)用擴(kuò)展函數(shù)時(shí), 在點(diǎn)號(hào)之前指定的對(duì)象實(shí)例)。
}
fun t_change_item() {
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)
println(l.toString())
}
/**
* 擴(kuò)展函數(shù)是靜態(tài)解析的
擴(kuò)展函數(shù)是靜態(tài)解析的蛹稍,并不是接收者類型的虛擬成員,在調(diào)用擴(kuò)展函數(shù)時(shí)部服,具體被調(diào)用的的是哪一個(gè)函數(shù)唆姐,由調(diào)用函數(shù)的的對(duì)象表達(dá)式來決定的,
而不是動(dòng)態(tài)的類型決定的:
*/
open class EC
class ED : EC()
fun EC.foo() = "c" // 擴(kuò)展函數(shù)foo
fun ED.foo() = "d" // 擴(kuò)展函數(shù)foo
/**
* 無論方法傳遞的是哪個(gè)實(shí)例廓八,最終只會(huì)調(diào)用EC的擴(kuò)展函數(shù)
*/
fun printFoo(c: EC) {
// 類型是c類型
// c
println(c.foo())
}
fun t_ext_method_2() {
// 這里會(huì)走EC的擴(kuò)展函數(shù)
printFoo(ED())
// 這里才會(huì)走ED的擴(kuò)展函數(shù)
println(ED().foo())
}
/**
* 若擴(kuò)展函數(shù)和成員函數(shù)一致奉芦,則使用該函數(shù)時(shí),會(huì)優(yōu)先使用成員函數(shù)剧蹂。
*/
class NC {
fun foo() {
println("成員函數(shù)")
}
}
fun NC.foo() {
println("擴(kuò)展函數(shù)")
}
fun t_ext_method_3() {
val nc = NC()
nc.foo()
}
/**
* 擴(kuò)展一個(gè)空對(duì)象
在擴(kuò)展函數(shù)內(nèi)声功, 可以通過 this 來判斷接收者是否為 NULL,這樣,即使接收者為 NULL,也可以調(diào)用擴(kuò)展函數(shù)
*/
fun Any?.toString(): String {
if (this == null)
return "null"
// 空檢測(cè)之后宠叼,this自動(dòng)轉(zhuǎn)換為非空類型先巴,所以下面的toString
// 解析為Any類的成員函數(shù)
return toString()
}
fun t_ext_nul_obj() {
var t = null
println(t.toString())
}
/**
* 擴(kuò)展屬性
除了函數(shù),Kotlin 也支持屬性對(duì)屬性進(jìn)行擴(kuò)展:
擴(kuò)展屬性允許定義在類或者kotlin文件中冒冬,不允許定義在函數(shù)中伸蚯。初始化屬性因?yàn)閷傩詻]有后端字段(backing field),所以不允許被初始化简烤,只能由顯式提供的 getter/setter 定義剂邮。
val Foo.bar = 1 // 錯(cuò)誤:擴(kuò)展屬性不能有初始化器
擴(kuò)展屬性只能被聲明為 val。
*/
val <T> List<T>.lastIndex: Int
get() = size - 1
/**
*
伴生對(duì)象的擴(kuò)展
如果一個(gè)類定義有一個(gè)伴生對(duì)象 横侦,你也可以為伴生對(duì)象定義擴(kuò)展函數(shù)和屬性挥萌。
伴生對(duì)象通過"類名."形式調(diào)用伴生對(duì)象,伴生對(duì)象聲明的擴(kuò)展函數(shù)枉侧,通過用類名限定符來調(diào)用
*/
class MyCls {
companion object {
// 將稱為Companion
}
}
// 擴(kuò)展函數(shù)
fun MyCls.Companion.foo() {
println("伴隨對(duì)象的擴(kuò)展函數(shù)")
}
// 擴(kuò)展字段
val MyCls.Companion.no: Int
get() = 10
fun t_ext_companion() {
println("no: ${MyCls.no}")
MyCls.foo()
}
/**
* 擴(kuò)展的作用域
* 通常擴(kuò)展函數(shù)或?qū)傩远x在頂級(jí)包下:
package foo.bar
fun Baz.goo() { …… }
要使用所定義包之外的一個(gè)擴(kuò)展, 通過import導(dǎo)入擴(kuò)展的函數(shù)名進(jìn)行使用:
package com.example.usage
import foo.bar.goo // 導(dǎo)入所有名為 goo 的擴(kuò)展
// 或者
import foo.bar.* // 從 foo.bar 導(dǎo)入一切
fun usage(baz: Baz) {
baz.goo()
}
*/
/**
* 擴(kuò)展聲明為成員
在一個(gè)類內(nèi)部你可以為另一個(gè)類聲明擴(kuò)展引瀑。
在這個(gè)擴(kuò)展中,有個(gè)多個(gè)隱含的接受者榨馁,其中擴(kuò)展方法定義所在類的實(shí)例稱為分發(fā)接受者伤疙,而擴(kuò)展方法的目標(biāo)類型的實(shí)例稱為擴(kuò)展接受者。
*/
// 擴(kuò)展接受者
class EN_D {
fun bar() {
println("EN_D bar")
}
}
// 分發(fā)接受者
class EN_C {
fun baz() {
println("EN_C baz")
}
fun EN_D.foo() {
println("EN_C ext foo")
bar()
baz()
}
fun caller(d: EN_D) {
println("EN_C caller")
// 調(diào)用擴(kuò)展函數(shù)
d.foo()
}
}
fun t_ext_mem() {
println("t_ext_mem")
val c: EN_C = EN_C()
val d: EN_D = EN_D()
c.caller(d)
}
/**
* 假如在調(diào)用某一個(gè)函數(shù)辆影,而該函數(shù)在分發(fā)接受者和擴(kuò)展接受者均存在徒像,則以擴(kuò)展接收者優(yōu)先,
* 要引用分發(fā)接收者的成員你可以使用限定的 this 語法蛙讥。
*/
class END {
fun bar() {
println("END bar")
}
}
class ENC() {
fun bar() {
println("ENC bar")
}
fun END.foo() {
println("ENC foo")
// 調(diào)用END.bar(),擴(kuò)展接收者優(yōu)先
bar()
this@ENC.bar() // 調(diào)用ENC.bar
}
fun caller(d: END) {
println("ENC caller")
// 調(diào)用擴(kuò)展函數(shù)
d.foo()
}
}
fun t_ext_mem_2() {
println("t_ext_mem_2")
val enc = ENC()
val end = END()
enc.caller(end)
}
// 調(diào)用 ->> 分發(fā)接收者 ->> 擴(kuò)展接收者
/**
* 以成員的形式定義的擴(kuò)展函數(shù), 可以聲明為 open , 而且可以在子類中覆蓋. 也就是說, 在這類擴(kuò)展函數(shù)的派 發(fā)過程中,
* 針對(duì)分發(fā)接受者是虛擬的(virtual), 但針對(duì)擴(kuò)展接受者仍然是靜態(tài)的锯蛀。
*/
open class EN2D {
}
class N2D : EN2D() {
}
open class EN2C {
open fun EN2D.foo() {
println("EN2D.foo in EN2C")
}
open fun N2D.foo() {
println("N2D.foo in EN2C")
}
fun caller(d: EN2D) {
println("EN2C caller")
// 調(diào)用擴(kuò)展函數(shù)
d.foo()
}
}
class N2C : EN2C() {
override fun EN2D.foo() {
println("EN2D.foo in N2C")
}
override fun N2D.foo() {
println("N2D.foo in N2C")
}
}
fun t_ext_mem_3() {
println(" t_ext_mem_3 ")
println(" -------------- ")
EN2C().caller(EN2D()) // EN2D.foo in EN2C
println(" -------------- ")
N2C().caller(EN2D()) // EN2D.foo in N2C
println(" -------------- ")
EN2C().caller(N2D()) // EN2D.foo in EN2C
}
class MyNewCls {
companion object {
val mFid1: Int = 1
var mFid2 = "this is myCFid2"
fun companionFun1() {
println("MyNewCls this is 1st companion func")
foo()
}
fun companionFun2() {
println("MyNewCls this is 2st companion func")
companionFun1()
}
}
fun MyNewCls.Companion.foo() {
println("MyNewCls 伴隨對(duì)象的擴(kuò)展函數(shù) (內(nèi)部) ")
}
fun test2() {
println("MyNewCls test2 ")
MyNewCls.foo()
}
init {
println("MyNewCls init ")
test2()
}
}
val MyNewCls.Companion.no: Int
get() = 10
fun MyNewCls.Companion.foo() {
println("MyNewCls.Companion foo 伴隨對(duì)象外部擴(kuò)展函數(shù)")
}
fun t_ext_mem_4() {
println("t_ext_mem_4")
println("no: ${MyNewCls.no}")
println("filed1:${MyNewCls.mFid1}")
println("filed2:${MyNewCls.mFid2}")
MyNewCls.foo()
MyNewCls.companionFun2()
}
/* -------------- 擴(kuò)展 -------------- */
fun main(args: Array<String>) {
t_user_print()
t_change_item()
t_ext_method_2()
t_ext_method_3()
t_ext_nul_obj()
t_ext_companion()
t_ext_mem()
t_ext_mem_2()
t_ext_mem_3()
t_ext_mem_4()
}
數(shù)據(jù)類和密封類
/* -------------- 數(shù)據(jù)類 -------------- */
/**
kotlin可以創(chuàng)建一個(gè)只包含數(shù)據(jù)的類:data
data class User(val name:String,val age:Int)
編譯器會(huì)自動(dòng)的從主構(gòu)造函數(shù)中根據(jù)所有聲明的屬性提取以下函數(shù):
equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 對(duì)應(yīng)于屬性,按聲明順序排列
copy() 函數(shù)
如果這些函數(shù)在類中已經(jīng)被明確定義了次慢,或者從超類中繼承而來旁涤,就不再會(huì)生成翔曲。
為了保證生成代碼的一致性以及有意義,數(shù)據(jù)類需要滿足以下條件:
主構(gòu)造函數(shù)至少包含一個(gè)參數(shù)劈愚。
所有的主構(gòu)造函數(shù)的參數(shù)必須標(biāo)識(shí)為val 或者 var ;
數(shù)據(jù)類不可以聲明為 abstract, open, sealed 或者 inner;
數(shù)據(jù)類不能繼承其他類 (但是可以實(shí)現(xiàn)接口)瞳遍。
*/
/**
* 復(fù)制
復(fù)制使用 copy() 函數(shù),我們可以使用該函數(shù)復(fù)制對(duì)象并修改部分屬性
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
*/
data class Account(val name: String, val age: Int)
fun t_data() {
var jack = Account(name = "Jack", age = 1)
// 復(fù)制并修改部分屬性
val olderJack = jack.copy(age = 2)
println(jack)
println(olderJack)
}
/**
* 數(shù)據(jù)類以及解構(gòu)聲明
* 組件函數(shù)允許數(shù)據(jù)在解構(gòu)聲明中使用
*/
fun t_data_dec_con() {
val jane = Account("jane", 35)
val (name, age) = jane
println("$name , $age years of age") // jane , 35 years of age
}
/**
* 標(biāo)準(zhǔn)數(shù)據(jù)類
標(biāo)準(zhǔn)庫(kù)提供了 Pair 和 Triple 菌羽。在大多數(shù)情形中掠械,命名數(shù)據(jù)類是更好的設(shè)計(jì)選擇,因?yàn)檫@樣代碼可讀性更強(qiáng)而且提供了有意義的名字和屬性注祖。
*/
/* -------------- 數(shù)據(jù)類 -------------- */
/* -------------- 密封類 -------------- */
/**
* 密封類用來表示受限的類繼承結(jié)構(gòu):當(dāng)一個(gè)值為有限幾種的類型, 而不能有任何其他類型時(shí)猾蒂。在某種意義上,他們是枚舉類的擴(kuò)展:
* 枚舉類型的值集合 也是受限的是晨,但每個(gè)枚舉常量只存在一個(gè)實(shí)例肚菠,而密封類 的一個(gè)子類可以有可包含狀態(tài)的多個(gè)實(shí)例。
聲明一個(gè)密封類罩缴,使用 sealed 修飾類蚊逢,密封類可以有子類,但是所有的子類都必須要內(nèi)嵌在密封類中箫章。
sealed 不能修飾 interface ,abstract class(會(huì)報(bào) warning,但是不會(huì)出現(xiàn)編譯錯(cuò)誤)
*/
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
/*
將類的聲明和定義該類的單例對(duì)象結(jié)合在一起(即通過object就實(shí)現(xiàn)了單例模式)
即將class關(guān)鍵字替換為object關(guān)鍵字时捌,來聲明一個(gè)類,與此同時(shí)也聲明它的一個(gè)對(duì)象炉抒。只要編寫這么多代碼奢讨,這個(gè)類就已經(jīng)是單例的了
換句話說,object declaration的類最終被編譯成:一個(gè)類擁有一個(gè)靜態(tài)成員來持有對(duì)自己的引用焰薄,并且這個(gè)靜態(tài)成員的名稱為INSTANCE拿诸,當(dāng)然這個(gè)INSTANCE是單例的,故這里可以這么去使用塞茅。如果用Java代碼來聲明這個(gè)RepositoryManager的話亩码,可以有如下代碼:
class RepositoryManager{
private RepositoryManager(){}
public static final RepositoryManager INSTANCE = new RepositoryManager();
}
*/
object NotANumber : Expr()
// 使用密封類的關(guān)鍵好處在于使用 when 表達(dá)式 的時(shí)候,如果能夠 驗(yàn)證語句覆蓋了所有情況野瘦,就不需要為該語句再添加一個(gè) else 子句了描沟。
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
class ObjectOuter {
object Inner {
fun method() {
println("I'm in inner class")
}
}
}
fun t_object_cls() {
ObjectOuter.Inner.method()
}
/* -------------- 密封類 -------------- */
fun main(args: Array<String>) {
t_data()
t_data_dec_con()
println("eval_1 ${eval(Const(35.0))}")
println("eval_2 ${eval(Sum(Const(35.0), Const(35.0)))}")
println("eval_2 ${eval(NotANumber)}")
t_object_cls()
}