泛型
泛型的定義
泛型是可以在類或方法中預(yù)支地使用未知的類型仓手。泛型的定義有多種,第一種是在定義的時(shí)候在類上面加上泛型聲明业踢,進(jìn)行使用的時(shí)候再確定泛型:
fun main(args: Array<String>) {
var box = Box<Apple>(Apple())
}
class Box<T>(var thing: T)
class Apple
第二種是定義專門的類繼承泛型類栗柒,確定泛型類型:
fun main(args: Array<String>) {
AppleBox(Apple())
}
open class Box<T>(var thing: T)
open class AppleBox(thing: Apple) : Box<Apple>(thing)//專門定義AppleBox
class Apple
第三種是父類定義有泛型,子類不知道具體類型知举,還可以再傳遞一個(gè)泛型給它:
class FruitBox<FRUIT>(thing:FRUIT):Box<FRUIT>(thing)
泛型函數(shù)
不僅類可以有類型參數(shù)瞬沦,函數(shù)也可以有。類型參數(shù)要放在函數(shù)名稱之前雇锡。要調(diào)用泛型函數(shù)逛钻,在調(diào)用處函數(shù)名之后指定類型參數(shù)即可:
fun main(args: Array<String>) {
fanxing("qwe")
}
fun <T> fanxing(thing: T) {
when (thing) {
is Int -> println("整形")
is String -> println("字符串")
is Char -> println("字符型")
}
}
泛型上限
泛型上線是最常見的約束類型,與 Java 的 extends 關(guān)鍵字相對(duì)應(yīng):
fun <T:Comparable<T>>sort(list:List<T>) {}
冒號(hào)之后指定的類型是上界:只有 Comparable<T> 的子類型可以替代 T 锰提。
泛型擦除
在泛型中無法獲取到泛型類型绣的,這叫做泛型擦除。
fun main(args: Array<String>) {
val box1 = Box<Int>(10)
val box2 = Box<String>("張三")
val clz1 = box1.javaClass.name
val clz2 = box2.javaClass.name
println(clz1)
println(clz2)
}
open class Box<T>(var thing: T)
在上面代碼中欲账,box1和box2的泛型類型不一樣,但是打印的結(jié)果是相同的芭概。想要解決這個(gè)問題赛不,需要在泛型前面加上reified,在泛型函數(shù)前加上inline:
inline fun <reified T>parse(thing:T):String{
return T::class.java.name
}
泛型類型投射
有下面一段代碼:
fun main(args: Array<String>) {
val list = arrayListOf(Thing())
}
//實(shí)物
open class Thing
//水果
open class Fruit: Thing()
//梨
class Pear: Fruit()
如果我們需要Arraylist里面只能傳遞Fruit或者子類罢洲,可以改為:
fun main(args: Array<String>) {
val list = arrayListOf(Thing())
parse(list)
}
fun parse(list:ArrayList<out Fruit>){
}
如果我們需要Arraylist里面只能傳遞Fruit或者父類踢故,可以改為:
fun main(args: Array<String>) {
val list = arrayListOf(Thing())
parse(list)
}
fun parse(list:ArrayList<in Fruit>){
}
其中:out 相當(dāng)于java的 ? extends;in 相當(dāng)于java的 ? super惹苗。
星號(hào)投射
如果我們希望ArrayList可以傳遞任何東西殿较,這時(shí)可以使用星號(hào)投射,也就是星投影:
fun main(args: Array<String>) {
}
fun parse(list:ArrayList<*>){
}
代碼中的" * "相當(dāng)于java的 " * "
中綴表達(dá)式
標(biāo)有 infix 關(guān)鍵字的函數(shù)也可以使用中綴表達(dá)式(忽略該調(diào)用的點(diǎn)與圓括號(hào))調(diào)用桩蓉。中綴函數(shù)必須滿足以下要求:
1.必須是成員函數(shù)和擴(kuò)展函數(shù)
2.必須只有一個(gè)參數(shù)
3.不能是可變參數(shù)
fun main(args: Array<String>) {
var p1 = person("張三",20)
p1 sayhelloto "李四"
}
class person(var name: String, var age: Int) {
infix fun sayhelloto(person: String) {
println("$name 給$person 打招呼")
}
}
注意:中綴表達(dá)式調(diào)用this不能省略:
fun hehe() {}
fun haha() {
this.hehe()
//中綴調(diào)用
this.sayhelloto("王五")
}
}
類委托
有以下案例:
圍裙媽媽只做飯不洗碗淋纲,小頭爸爸洗碗一次十元,大頭兒子洗碗一次三元院究。圍裙媽媽讓小頭爸爸洗碗本涕,小頭爸爸委托給大頭兒子洗。小頭爸爸和大頭兒子都有洗碗的功能晦闰,但是小頭爸爸把洗碗的能力委托給了大頭兒子實(shí)現(xiàn),這就是類委托。類委托有兩種形式醒串,第一種是指定委托,即只委托給大頭兒子實(shí)現(xiàn):
fun main(args: Array<String>) {
var big = BigHeadSon()
var small = SmallHeadFather()
big.wash()
small.wash()
}
//實(shí)現(xiàn)洗碗的功能
interface WashPower {
fun wash()
}
//大頭兒子實(shí)現(xiàn)洗碗的功能
class BigHeadSon : WashPower {
override fun wash() {
println("大頭兒子開始洗碗")
}
}
//小頭爸爸委托大頭兒子洗碗
class SmallHeadFather : WashPower by BigHeadSon()
第二種委托:只要有洗碗功能都可以實(shí)現(xiàn):
fun main(args: Array<String>) {
var big = BigHeadSon()
var small = SmallHeadFather(big)
big.wash()
small.wash()
}
//實(shí)現(xiàn)洗碗的功能
interface WashPower {
fun wash()
}
//大頭兒子實(shí)現(xiàn)洗碗的功能
class BigHeadSon : WashPower {
override fun wash() {
println("大頭兒子開始洗碗")
}
}
//小頭爸爸委托大頭兒子洗碗->第一種委托
//class SmallHeadFather : WashPower by BigHeadSon()
class SmallHeadFather(washPower: WashPower):WashPower by washPower
屬性委托
屬性委托就是把屬性的get和set方法委托給其他對(duì)象。
過年時(shí),爺爺奶奶給了大頭兒子壓歲錢,圍裙媽媽替大頭兒子保管肛宋。就屬于屬性委托:
fun main(args: Array<String>) {
var bigheadson = BigHeadSon()
bigheadson.money = 100
println(bigheadson.money)
}
class WeiQunMom {
//存儲(chǔ)兒子的壓歲錢
var son = 0//兒子的壓歲錢
var mom = 0//自己的存錢罐
//委托壓歲錢的get方法
operator fun getValue(bigHeadSon: BigHeadSon, property: KProperty<*>): Int {
return son
}
//委托壓歲錢的set方法
operator fun setValue(bigHeadSon: BigHeadSon, property: KProperty<*>, i: Int) {
son += 20
mom += i - 20
}
}
class BigHeadSon {
var money: Int by WeiQunMom()
}
加載
惰性加載
需要使用的時(shí)候才加載毁涉,屬于懶加載遇西。用by lazy關(guān)鍵字。惰性加載需要注意以下幾點(diǎn):
1.by lazy用在字段压彭,必須val修飾
2.by lazy返回值在最后一行
3.by lazy只會(huì)加載一次
4.對(duì)象里面也可以使用by lazy
5.惰性加載只要調(diào)用就會(huì)都會(huì)加載,所以在需要的時(shí)候再進(jìn)行加載
fun main(args: Array<String>) {
println(name)
println(name)
}
val name by lazy {
println("執(zhí)行了加載1")
"加載1"
}
class D {
val name by lazy {
println("執(zhí)行了加載2")
"加載2"
}
}
輸出結(jié)果是
執(zhí)行了加載1
加載1
加載1
可見:
by lazy只會(huì)初始化一次
by lazy最后一行為返回值
by lazy是線程安全的
延遲加載
延遲加載所用到的關(guān)鍵字是lateinit,不確定具體值,var可變缩功,且獨(dú)立于類單獨(dú)存在:
fun main(args: Array<String>) {
val person = Person()
person.name = "張三"
person.sayHello()
}
lateinit var name: String
class Person {
//現(xiàn)在不知道name對(duì)應(yīng)的數(shù)據(jù) 后面初始化的時(shí)候才能確定
//延遲初始化
lateinit var name: String
fun sayHello() {
println(name)
}
}
這段代碼中的name在person類中沒有賦值,在main方法中才予以賦值。若name沒有賦值跟压,沒有賦值直接訪問會(huì)報(bào)錯(cuò):UninitializedPropertyAccessException茸塞。
擴(kuò)展函數(shù)
擴(kuò)展函數(shù)是在不改變已有類的情況下,為類添加新的函數(shù)倔监。主要是為了替代java中的util類静暂。父類定義的擴(kuò)展函數(shù),子類也可以使用。
fun main(args: Array<String>) {
//可空類型
val str: String? = null
//判斷str是否為空
var res = str?.IsEmpty() ?: true
}
//給String類型擴(kuò)展了一個(gè)IsEmpty函數(shù)
fun String.IsEmpty(): Boolean {
return this == null || this.length == 0
}
對(duì)于擴(kuò)展函數(shù)來說吉执,如果通過父類接收鼠证,那么執(zhí)行的是父類的擴(kuò)展函數(shù):
fun main(args: Array<String>) {
val view = View()
val button:Button = Button()
button.onclick()
}
fun View.onclick(){
println("點(diǎn)擊了view")
}
fun Button.onclick(){
println("點(diǎn)擊了button")
}
open class View
class Button:View()
object單例
單例的原理:通過靜態(tài)代碼塊創(chuàng)建Person對(duì)象實(shí)例峡竣,用的時(shí)候可以使用這個(gè)實(shí)例。object單例每一個(gè)字段都是靜態(tài)的量九,比較消耗內(nèi)存适掰。object單例適用于成員變量比較少的情況:
fun main(args: Array<String>) {
Person.sayHello()
Person.sayHello()
}
object Person{
var name = "張三"
var age = "20"
var phone = "123"
fun sayHello(){
println("hello")
}
}
伴生對(duì)象
可以將定義成static靜態(tài)的屬性放在伴生對(duì)象里,外部類可以直接調(diào)用伴生對(duì)象里面的方法和屬性荠列。伴生對(duì)象的字段都是靜態(tài)的:
class Person{
var name = "張三"
companion object {
var phone = "123444"
}
}
枚舉
枚舉類的最基本的用法是實(shí)現(xiàn)類型安全的枚舉类浪,枚舉里面保存的就是有限的實(shí)例對(duì)象:
fun todo(week: Week) {
when (week) {
Week.星期一 -> println("工作")
Week.星期二 -> println("工作")
Week.星期三 -> println("工作")
Week.星期四 -> println("工作")
Week.星期五 -> println("工作")
Week.星期六 -> println("休息")
Week.星期日 -> println("休息")
}
}
enum class Week {
星期一, 星期二, 星期四, 星期五, 星期六, 星期日, 星期三
}
枚舉的高級(jí)用法
以RGB顏色為例:
RED r:255 g:0 b:0
GREEN r:0 g:255 b:0
BLUE r:0 g:0 b:255
fun main(args: Array<String>) {
println(Color.RED.r)
}
enum class Color(var r:Int,var g:Int,var b:Int){
RED(255,0,0),GREEN(0,255,0),BLUE(0,0,255)
}
數(shù)據(jù)類
我們經(jīng)常創(chuàng)建一些只保存數(shù)據(jù)的類。 在這些類中肌似,一些標(biāo)準(zhǔn)函數(shù)往往是從數(shù)據(jù)機(jī)械推導(dǎo)而來的费就。在 Kotlin 中,這叫做數(shù)據(jù)類并標(biāo)記為data川队。數(shù)據(jù)類只保存數(shù)據(jù)力细,沒有其他任何邏輯操作,對(duì)應(yīng)java的bean類固额。數(shù)據(jù)類相當(dāng)于提供了bean類的模板眠蚂,可以替代java的bean:
fun main(args: Array<String>) {
val news = News("d:/path/a.jpg","標(biāo)題","簡(jiǎn)介")
news.component1()//對(duì)應(yīng)imgPath
news.component2()//對(duì)應(yīng)title
news.component3()
}
data class News(var imgPath:String,var title:String,var desc:String)
密封類
密封類可以理解為增強(qiáng)型的枚舉。枚舉在意數(shù)據(jù)斗躏,而密封類更在意類型逝慧。所以,密封類密封的是類型:
fun main(args: Array<String>) {
println(isLegel(JonSnow()))
}
fun isLegel(stark: NedStark): Boolean {
when (stark) {
is NedStark.AryaStark -> return true
is NedStark.RobStark -> return true
is NedStark.SansaStark -> return true
is NedStark.BrandonStark -> return true
else -> return false
}
}
sealed class NedStark {
class RobStark : NedStark()
class SansaStark : NedStark()
class AryaStark : NedStark()
class BrandonStark : NedStark()
}
class JonSnow : NedStark()
class KonSnow : NedStark()