學習Kotlin已經有兩年了,從今年1月開始實際投入項目使用,自認爲對於Kt使用的還算熟練。html
我的認爲Kotlin真的是一門越用越爽的語言,但在實際開發中,剛開始常常會出現忽然忘記了某個特性或者某個高階函數的使用方式,又得去google,這個時候 若是有一份筆記,可讓你直接搜索到想要的,那麼該有多好呢,這就是這份筆記的做用。java
本文快捷查找連接,歡迎一鍵轉存,下載,或者隨時搜索,已是MD格式了,使用 typora 盡情本地享用吧!android
這份總結實際上是兩篇的合集,第一篇是於2018年初學習,當時學完以後,總體上實際是懵逼的狀態,由於當時沒有實際去使用,因此在2019年末隨着工做的須要,又從新學習了一遍。固然從新學習至關於挑着看,對於一些感興趣的篇幅瞭解一下便可,更多的仍是在於實際應用。因此你會發現,這篇筆記前期皆爲基礎之談,中後期開始深刻實際使用。git
在這裏,特別感謝 bennyhuo ,是它的課程使我踏進了Kotlin的大門,有一說一,很是棒。github
一些看過的書的我的感覺:編程
Kotlin核心編程數組
水滴技術團隊出的,從Kotlin的背景到各類使用細節都有概述,說它是一本神書(工具書,No)不足爲過。瀏覽器
Kotlin編程實戰安全
提升實際開發效率的一本書,若是是中期去看,提升可能會很多,若是已經實際使用Kotlin許久,文中的技巧又能加深你幾分理解,即從使用轉化爲 **哦,**原來如此。markdown
深刻理解Kotlin協程-bennyhuo大佬的書
中前期不推薦,難度太高,極可能從憧憬這本書變爲罵罵咧咧的塞到顯示器底下。
若是有人說文檔很差,我只能很遺憾的說,你錯過了一片玉米地。
不錯不錯,味道好極了,開玩笑,真的很是好。
歡迎查看個人筆記,CloudBook,裏面詳細記錄了我所學所經歷的各類。
做爲一個開發者,保持不斷學習的習慣是必不可少的。咱們沒法改變時代的走向,但至少別被遠遠拉在後面,咱們可能沒法改變咱們的學歷,家室,但將來是由咱們本身來決定的。作一個永遠的學習者,永遠保持對技術的熱愛。
這裏只能摘取一部分,由於字數限制。
Kotlin就是一門能夠運行在 JAVA虛擬機,Android,瀏覽器上的靜態語言,它與Java100%兼容,若是你對Java很是熟悉,那麼你就會發現Kotlin除了本身的標題庫以外,大多任然使用經典的Java 集合框架。
學習目標
1. 學會使用 Kotlin
2. 熟悉Java生態
3. 瞭解一些特性背後的實現
複製代碼
var與val 的區別
var爲可變變量,val至關於只讀變量,如同java 中的final 同樣,val 建立時必須被初始化。
複製代碼
1.認識基本類型 2.初步認識類及其相關概念 3.認識區間和數組
Boolean
var a: Boolean=true
var b: Boolean=false
a爲參數
複製代碼
Number
var a: Int =8
var a: Int =0xFF
複製代碼
Float
Float類型後面必須加F
NaN /不是數
val f1: Float =8.0F
val Positive: Float = Float.POSITIVE_INFINITY 正無窮
val Negative Float.NEGATIVE_INFINITY 負無窮
複製代碼
Short 短整型
Byte
int anInt = 5
Integer anInteger = 5
在Kotlin 裏面,Int實際是 int與Integer的合體,在必要的時候,編譯器會自動幫咱們進行分辨。
複製代碼
不可隱式轉換
不能直接像Java裏同樣,將整型賦給 Long,在Kotlin 裏,須要顯示調用toLong()方法
val anInt : Int =5
val aLong : Long = abInt.toLong()
複製代碼
== 比較內容,即相似Java 的equals
=== 比較對象是否相同
複製代碼
//字符串模板 $
val a: Int = 1
val b: Int = 2
println("" + a + "+" + b + "=" + (a + b))
println("$a+$b=${a+b}")
若是想在字符串中添加雙引號,須要添加轉義字符
Petterp 「123」
var string:String="Petterp \"123\""
須要打印 $ 符號
var money:String="1000"
println("$$money")
多行字符串,回車直接能夠換行,trimIndent()去除字符串左邊爲空的位置,這裏面沒法使用轉義
var raw:String=""" 123 456 """.trimIndent()
println(raw)
複製代碼
//一個繼承的例子
/*open至關於打開,容許被繼承 主構造函數不能包含任何的代碼。初始化的代碼能夠放到以 init 關鍵字做爲前綴的初始化(initializerblocks)中。 在實例初始化期間,初始化塊按照它們出如今類體中的順序執行,與屬性初始化器交織在一塊兒:*/
//constructor 主構造函數
class demo1 constructor(a: String, b: String) : Demo(a, b)
class demo2 constructor(a: String, b: String) : Demo(a, b)
open class Demo (a: String,b: String) {
init {
//獲取類的名字
println("${this.javaClass.simpleName} $a $b")
}
}
fun main(args: Array<String>) {
var dd1: demo2 = demo2("a", "b")
}
open 覆蓋方法也須要加
override 子類覆蓋時須要加
複製代碼
java 代碼
public class MyClass {
public static void main(String[] a){
System.out.println(demo().length());
}
public static String demo(){
return null;
}
}
Kotlin 代碼
fun demo(): String? {
//若是加了 ?,編譯器就會容許返回null,不然直接報錯
return null
}
fun main(args: Array<String>) {
//?的意思是,若是爲空,則執行前半句,不然執行後半句打印長度
println(demo()?.length)
val a: String? = "123"
//!! 告訴編譯器容許編譯器忽略空類型安全
println(a!!.length)
}
複製代碼
//父類強轉爲子類測試
//繼承關係,Chaid繼承自Parent
java
public class Test {
public static void main(String[] args) {
Parent parent=new Parent();
((Chaid) parent).pp();
}
}
Kotlin
fun main(args: Array<String>) {
//父類強轉爲子類測試
val parent: Parent = Parent()
//若是parent轉換失敗,則返回null,程序而不會崩潰
val chaid: Chaid? = parent as? Chaid
//此時打印即爲null
println(chaid)
}
複製代碼
fun main(args: Array<String>) {
val range:IntRange=0..1024 //[0,1024]
val ranege_exclusive:IntRange=0 until 1024 //[0,1024)
val range_min:IntRange= 0..-1
//判斷是否是空
println(range_min.isEmpty())
//判斷是否包含1024
println(ranege_exclusive.contains(1024))
//寫法不一樣,contains 內部也是用的下面這種方法
println(1024 in range)
//迭代--至關於輸出range數組
for (i in range){
print("$i,")
}
}
複製代碼
在Kotlin裏面,基本類型的數組,都是定製的,目的是爲了不沒必要要的裝箱與拆箱,節省效率
基本寫法
val array: Array = arrayOf(...)
基本操做
val arrayOfInt: IntArray = intArrayOf(1, 3, 5)
val arrayOfChar: CharArray = charArrayOf('H', 'e', 'l', 'l', 'o')
val arrayOfString: Array<String> = arrayOf("Petterp", "p")
fun main(args: Array<String>) {
//for迭代
for (int in arrayOfInt) {
println(int)
}
//返回[0,2)區間的元素
println(arrayOfString.slice(0 until 2))
//使用分隔符建立一個字符串
println(arrayOfChar.joinToString(""))
//將指定數組中的字符轉換爲字符串
println(String(arrayOfChar))
//打印類的全名
println(Kotlin2::class.java.name)
//打印類名區間
println(Kotlin2::class.java.simpleName.slice(0 until 2))
//基本類型轉換
val hello:Long= arrayOfChar.size.toLong()
println(hello)
}
class Kotlin2 {
}
複製代碼
val data: String = "Petterp"
val data2 = data
//類型推導,編譯器已經知道它的類型是 Int
val data3 = 1
fun main(args: Array<String>) {
println(data)
println(data2)
println(data3)
}
複製代碼
如今添加 const
const val data: String = "Petterp"
const val data2 = data
//類型推導,編譯器已經知道它的類型是 Int
val data3 = 1
fun main(args: Array<String>) {
println(data)
println(data2)
println(data3)
}
複製代碼
因此這就是編譯器常量的做用。編譯器常量的引用都是直接指明該變量的值
//函數寫法
fun Demo1(a: Int): Int {
return a
}
//當函數返回一個表達式的值是,可使用一下方式
fun Demo2(a: Int) = a
//匿名函數,須要賦值給一個函數變量
val k = fun(a: Int): Int {
return a
}
fun main(args: Array<String>) {
println(Demo1(100))
println(Demo2(200))
println(k(300))
}
複製代碼
注意事項
什麼是Lambda表達式
Lambda 類型表示
Lambda 表達式的調用
用 () 進行調用
等價於 invoke()
舉例
val sum{a:Int,b:Int -> a+b}
調用
sum(1,2)
sum.invoke(1,2)
複製代碼
Lambda 表達式的簡化
//Lambda
//原始寫法
args.forEach ({ println(it) })
//對於函數來講,若是最後一個參數是Lambda表達式,小括號能夠移到外面
args.forEach(){ println(it) }
//若是小括號裏什麼都沒有,能夠刪掉小括號
args.forEach { println(it)}
//若是傳入的這個函數和須要接收的Lambda表達式類型同樣,那麼進一步簡化
args.forEach (::println)
複製代碼
什麼是類成員
函數和方法的區別
定義方法
寫法與普通函數徹底一致
class A {
//簡短寫法,沒有返回值類型的狀況下
fun say(name: String) = println("Hello $name")
fun phone(phone:String):String{
return phone
}
}
複製代碼
定義屬性
構造方法參數中 val / var 修飾的都是屬性
類內部也能夠定義屬性
// 加修飾的爲屬性,b只是普通的一個構造方法參數
class A(val a: Int, b: Int) {
var b = 0
}
複製代碼
屬性訪問控制
屬性能夠定義 getSet / setter
class B{
//val 修飾的 不可變 無set方法
val demo:Int=0
/* set(value) { }*/
var demo2:Int=0
set(value) {
println(demo2)
field = value
}
}
複製代碼
屬性初始化
屬性的初始化儘可能在構造方法中完成
沒法在構造方法中初始化,嘗試降級爲局部變量
var 用 lateinit 延遲初始化,val 用 lazy
可空類型謹慎用 null 直接初始化
class X
class A(val a: Int, b: Int) {
var b = 0
//laterinit 延遲初始化
lateinit var c: String
lateinit var d: X
val e: X by lazy {
println("Start X")
X()
}
var cc: String? = null //不推薦這種寫法
}
複製代碼
任意類能夠定義或者重載父類的基本運算符
經過運算符對應的具名函數來定義
對於參數個數作要求,對參數和返回值類型不作要求
不能像Scala同樣定義任意運算符
//使用operator關鍵字能夠重載基本運算符,好比下面的plus函數加上operator,就至關於基本運算中的 +
//運算符重載要求與運算符的函數名對應,好比要重載加法,函數名就必須是 plus
class Complex(var real:Int,var imaginzry:Int){
operator fun plus(other:Complex):Complex{
return Complex(real+other.real,imaginzry+other.imaginzry)
}
operator fun plus(other: Int):Complex{
return Complex(real+other,imaginzry)
}
operator fun plus(other: Any):Int{
return real.toInt()
}
override fun toString(): String {
return "$real+$imaginzry"
}
}
class Book{
//infix 中指表達式,不用點括號去調用
infix fun on(any:Any):Boolean{
return false
}
}
class Desk
fun main(args: Array<String>) {
val c1=Complex(3,4)
val c2=Complex(1,2)
println(c1+c2)
println(c1+5)
println(c1+"Petterp")
//-name <Name>
//in 表示有這個元素返回1 不然返回-1
if ("-name" in args){
println(args[args.indexOf("-name")+1])
}
}
複製代碼
中綴表達式
只有一個參數,且用 infix 修飾的函數
//infix(中綴表達式) 定義的函數沒必要動過.xx() 的形式調用,而是能夠經過函數名(沒必要寫括號) 調用。這種寫法在Dsl 中比較常見,在代碼中慎用,影響可讀性
class Book{
//infix
infix fun on(any:Any):Boolean{
return false
}
}
class Desk
fun main(args: Array<String>) {
if(Book() on Desk()){
}
}
複製代碼
if表達式
if ..else 同java 同樣,可是Kotlin 裏面具備返回值,因此稱爲表達式
表達式與完備性 (即在用 if表達式 賦值時,全部條件都必須完整)
val x=if(b<0) 0 else b
val x=if(b<0) 0 //錯誤,賦值時,分支必須完備
複製代碼
when表達式
增強版 switch,支持任意類型
支持春表達式條件分支(相似 if)
表達式與完備性
fun main(args: Array<String>) {
val x=99
val b=when(x){
in 1..100 -> 10
!in 1..100 -> 20
args[0].toInt() -> 30
else -> x
}
when(x){
in 1..100 -> println("ok")
else -> println("no")
}
// val mode=when{
// args.isNotEmpty()&& args[0]=="1" ->1
// else ->0
// }
println(b)
}
複製代碼
fun main(args: Array<String>) {
for((i,value) in args.withIndex()){
println("$i -> $value")
}
for(i in args.withIndex()){
println("${i.index} -> ${i.value}")
}
}
複製代碼
try...catch
catch 分支匹配異常類型
能夠寫爲表達式,用來賦值
val a=try {
100/10
}catch (e:Exception){
0
}
複製代碼
finally
finallu 不管代碼是否拋出異常都會執行
//注意下面的寫法finaly仍是會先執行,最後纔是 return
fun P(): Int {
//try 表達式
return try {
100 / 10
} catch (e: Exception) {
0
} finally {
println("我先打印")
}
複製代碼
具名參數
給函數的實參附上形參
//這樣寫的話,參數的位置就不會產生影響
fun main(args: Array<String>) {
sum(b=1,a=2)
}
fun sum(a: Int, b: Int) {
println("a=$a b=$b")
}
複製代碼
變長參數
某個參數能夠接受多個值
能夠不爲最後一個參數
若是傳參時有歧義,須要使用具名參數
fun main(vararg: Array<String>) {
sum(1, 2, 3, 4, b = "B")
}
fun sum(vararg a: Int, b: String) {
a.forEach(::println)
println(b)
}
複製代碼
Spread Operator
只支持展開 Array
只用於變長參數列表的實參
不能重載
不能算通常的運算符
fun main(vararg: Array<String>) {
sum(1, 2, 3, 4, b = "B")
//Spread Operator,只支持Array
val array= intArrayOf(1,3,4,5)
sum(*array,b="B")
}
fun sum(vararg a: Int, b: String) {
a.forEach(::println)
println(b)
}
複製代碼
默認參數
爲函數參數指定默認值
能夠爲任意位置的參數指定默認值
傳參時,若是有歧義,須要使用具名參數
fun main(vararg: Array<String>) {
//調用者未傳值,使用的是默認值
sum(b="B")
sum(1,"B")
}
//若是有個函數常用一個值,那麼在其聲明的時候就能夠指定這個值
fun sum(a: Int = 0, b: String) {
println("a=$a b=$b")
}
複製代碼
什麼是接口
接口,直觀理解就是一種約定
interface A {
fun Print()
}
//接口繼承
interface A1 : A {
//也能夠定義一個變量,這裏實際至關於方法,可是沒法有set,get方法
var a: Int
fun ko() {
println(a)
}
}
class TestA(override var a: Int) : A1 {
override fun Print() {
println("Petterp")
}
}
fun main() {
val test = TestA(1)
test.Print()
test.ko()
}
複製代碼
接口
抽象類
實現了一部分協議的半成品
能夠有狀態,能夠有方法實現
必須由子類繼承後使用
interface P1
interface P2 : P1
abstract class A {
//打印出類名
fun Print() = println(javaClass.simpleName)
}
class B : A(), P2
fun main() {
//不能夠這樣用,由於沒有子類繼承
// val a = A
//關係能夠這樣來寫
val p1: P1 = B()
val p2: P2 = B()
val a: A = B()
val b: B = B()
b.Print()
}
複製代碼
抽象類與接口的共性
抽象類和接口的區別
父類須要open 才能夠被繼承
父類方法,屬性須要open 才能夠被覆寫
接口,接口方法,抽象類默認爲 open
覆寫父類 (接口) 成員 須要 onverride 關鍵字
class D:A() , B ,C
複製代碼
注意繼承類時實際上調用了父類構造方法
類只能單繼承,接口能夠多實現
//繼承與子類重寫父類的Demo
abstract class Person(open val age: Int) {
abstract fun work()
}
class MaNong(age: Int) : Person(age) {
//重寫屬性
override val age:Int
get() = 0
override fun work() {
println("我是碼農")
}
}
class Doctor(age: Int) : Person(age) {
override fun work() {
println("我是醫生")
}
}
fun main() {
val manong = MaNong(20)
val doctor = Doctor(100)
println(manong.age)
println(doctor.age)
}
複製代碼
接口代理
接口方法實現交給代理類實現
class Manager(driver:Driver):Driver by driver
//接口代理的Demo
interface AA{
fun Print1()
}
interface BB{
fun Print2()
}
class Car:AA{
override fun Print1() {
println("AA")
}
}
class Bar:BB{
override fun Print2() {
println("BB")
}
}
//
//class AB:AA,BB{
// override fun Print1() {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
// override fun Print2() {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
// }
//
//}
//by 接口代理
class SendWill(val aa:AA,val bb:BB):AA by aa,BB by bb
fun main() {
val aa=Car()
val bb=Bar()
val send=SendWill(aa,bb)
send.Print1()
send.Print2()
}
複製代碼
接口方法衝突
接口方法能夠有默認實現
簽名一致且返回值相同的衝突
子類(實現類) 必須覆寫衝突方法
super <[父類 (接口) 名 ]>.[方法名]([參數列表]
複製代碼
Demo
interface A {
fun setT() = "接口A"
}
interface B {
fun setT() = "接口B"
}
abstract class C {
open fun setT() = "抽象類C"
}
class Demo(val a: Int) : A, B, C() {
override fun setT(): String {
return when (a) {
1 -> super<A>.setT()
2 -> super<B>.setT()
3 -> super<C>.setT()
else -> ""
}
}
}
fun main() {
println(Demo(1).setT())
println(Demo(2).setT())
}
複製代碼
只有一個實例的類
不能自定義構造方法
能夠實現接口,繼承父類
本質上就是單例模式最基本的實現
Koltin--
interface A {
fun a1() } abstract class C object KotlinDan : C(), A {
override fun a1() {
println("Kotlin")
}
}
Java--
public class Kotlin_java {
public static void main(String[] args) {
//引用Kotlin代碼
KotlinDan.INSTANCE.a1();
//在java裏面
JavaDan.inter.Ag();
}
}
//java中的簡易單例例子
class JavaDan{
static JavaDan inter=new JavaDan();
private JavaDan(){}
void Ag(){
System.out.println("Java");
}
}
複製代碼
每一個類能夠對應一個伴生對象
伴生對象的成員全局獨一份(對於類來講)
伴生對象的成員相似 Java 的靜態成員
在Kotlin中,靜態成員考慮用包級函數,包級變量替代
JvmField 和 JvmStatic 的使用
Kotlin--
class Demo private constructor(val value:Double){
//伴生對象
companion object {
@JvmStatic
fun A(double: Double): Demo {
return Demo(double)
}
@JvmField
val TAG:String="Petterp"
}
}
fun main() {
println(Demo.A(1.1).value)
}
Java--
public class KotlinJava {
public static void main(String[] args) {
//java代碼,未添加 @JvmStataic 與 JvmField
Demo demo = Demo.Companion.A(1.1);
String tag = Demo.Companion.getTAG();
//添加後
Demo demo1 = Demo.A(1.1);
String tag1=Demo.TAG;
}
}
複製代碼
方法重載
Overloads
名稱相同,參數不一樣的方法
Jvm函數簽名的概念 : 函數名,參數列表
跟返回值沒有關係
class A{
fun a():Int{
return 0
}
fun a(int: Int):Int{
return int
}
//方法重載與返回值無關
/* fun a():String{ }*/
}
fun main() {
val a=A().a()
val a2=A().a(123)
}
複製代碼
默認參數
爲函數參數設定一個默認值
能夠爲任意位置的參數設置默認值
函數調用產生混淆時用具名參數
Kotlin--
class A {
// fun a():Int{
// return 0
// }
// fun a(int: Int):Int{
// return int
// }
//使用具名參數替代上面的重載方法
@JvmOverloads
fun a(int: Int = 0): Int {
return int
}
}
fun main() {
val a = A().a()
val a2 = A().a(123)
}
Java調用時--
public class KotlinJava {
public static void main(String[] args) {
A a=new A();
//在Kotlin代碼上添加 @JvmOverloads 就可讓java識別具名參數
a.a();
a.a(123);
}
}
複製代碼
方法重載與默認參數的關係
兩者的相關性以及 @JvmOverloads
避免定義關係不大的重載
很差的設計好比:
Java代碼中的一個Bug
public class Bug {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(10);
list.add(20);
System.out.println(list);
//異常,數組越界
list.remove(10);
list.remove(2);
}
}
在Kotlin代碼中
fun main() {
val list = ArrayList<Int>()
list.add(1)
list.add(2)
list.add(10)
list.add(20)
println(list)
list.removeAt(10)
list.remove(2)
}
複製代碼
爲現有類添加方法,屬性
fun X.y():Z {...}
val X.m 注意擴展屬性不能初始化,相似接口屬性
複製代碼
Java 調用擴展成員相似調用靜態方法
operator fun String.times(int: Int):String{
val stringBuilder=StringBuilder()
for (i in 0 until int){
stringBuilder.append(this)
}
return stringBuilder.toString()
}
val String.op:String
get() = "123"
fun main() {
println("abc".times(16))
//擴展方法前加了operator,重載運算符
println("abc"*16)
println("abc".op)
}
java調用
public class A {
public static void main(String[] args) {
System.out.println(Kotlin2Kt.times("abc",16));
System.out.println(Kotlin2Kt.getOp("Petterp"));
}
}
複製代碼
定義方法
val/var <property name>: <Type> by <experession>
//翻譯 property-屬性 type-類型 experssion-函數調用
複製代碼
代理者須要實現相應的 setValue/getValue 方法
// Demo--
class A{
val p1:String by lazy {
"123"
}
//屬性代理,實際調用getValue
val p2 by X()
//須要實現setValue
var p3 by X()
}
class X{
private var value: String? =null
operator fun getValue(thisRef:Any?,property:KProperty<*>):String{
return value?:""
}
operator fun setValue(thisRef: Any?,property: KProperty<*>,string: String){
this.value=string
}
}
fun main() {
val a=A()
println(a.p1)
println(a.p2)
//此時爲空,由於未設置值
println(a.p3)
a.p3="123"
println(a.p3)
}
複製代碼
再見,JavaBean
默認實現的copy,toString 等方法
componentN 方法
allOpen 和 noArg 插件 (設計的角度來講,data class不容許有子類,因此若是你要改寫的話,須要用到這兩個插件,會在編譯期經過修改字節碼的方式去掉 final關鍵字,並增長一個無參構造方法)
data class 初始化的時候,必定要給它的屬性賦值(帶參數),即它並無默認的無參構造方法
//加了data 以後,自動實現各類方法,可查看字節碼發現
data class A(val id:Int,val name:String)
class ComponentX{
//手動實現component1()
operator fun component1():String{
return "PP"
}
operator fun component2():Int{
return 0
}
}
fun main() {
println(A(1,"Petterp"))
val (a,b)=ComponentX()
println("a=$a,b=$b")
//打印結果
/*A(id=1, name=Petterp) a=PP,b=0*/
}
複製代碼
allOpen noArg 配置方法
複製代碼
內部類
定義在類內部的類
與類成員有類似的訪問控制
默認是靜態內部類,非靜態用 inner 關鍵字
this@Outter , this@Inner 的用法
若是內部類依賴外部類,那麼使用非靜態內部類,不然反之
class A {
var int:Int = 5
//加了inner 爲非靜態內部類
inner class A1 {
var int:Int = 10
fun Print(){
//內部類的變量
println(int)
//外部變量
println(this@A.int)
}
}
}
複製代碼
匿名內部類
沒有定義名字的內部類
類名編譯時生成,相似 Outter$1.class (看起來沒有名字,實際編譯時有本身的Id)
可繼承父類,實現多個接口,與Java 注意區別
interface OnclickListener {
fun onclick()
}
class View {
var onclickListener: OnclickListener? = null
}
open class A
fun main() {
val view=View()
view.onclickListener=object : A(),OnclickListener{
override fun onclick() {
}
}
}
複製代碼
實例可數的類,注意枚舉也是類
能夠修改構造,添加成員
能夠提高代碼的表現力,也有必定的性能開銷
//枚舉類也是有構造方法的,咱們能夠在它的構造方法中傳入參數
enum class LogLevel(val id:Int){
A(1),B(2),C(3),D(4),E(5) ;
//須要分號隔開
fun getTag():String{
return "$id,$name"
}
override fun toString(): String {
return "$name,$ordinal"
}
}
//LogLevel 更像是 LogLevel2的語法糖
//它們兩個是等價的
class LogLevel2(val id:Int) private constructor(){
//伴生對象寫法
companion object {
val A=LogLevel2(1)
val B=LogLevel2(2)
val C=LogLevel2(3)
val D=LogLevel2(4)
val E=LogLevel2(5)
}
constructor(i: Int) : this()
}
fun main() {
println("${LogLevel.A},${LogLevel.A.ordinal}")
println(LogLevel.A.getTag())
//遍歷枚舉,重寫了toString()方法
LogLevel.values().map(::println)
//返回"A"的實例
println(LogLevel.valueOf("A"))
}
複製代碼
子類可數 (枚舉是實例可數)
要注意密封類與枚舉的不一樣,看如下Demo
//在如下Demo中,這是一個音樂播放Demo
//須要不一樣指令及不須要參數的地方咱們能夠用枚舉實現,而那些須要不一樣指令參數的地方咱們用枚舉就沒法實現了
//sealed的子類只能繼承在與Sealed同一個文件當中,或者做爲它的內部類
sealed class PlayerCmd {
class Play(val yrl: String, val postition: Long = 0) : PlayerCmd()
class Seek(val postition: Long) : PlayerCmd()
object Pause : PlayerCmd()
object Resume : PlayerCmd()
object Stop : PlayerCmd()
}
enum class PlayerState{
IDLE,PAUSE,PLAYING
}
複製代碼
傳入或者返回函數的函數
函數引用 ::println
帶有 Receiver 的引用, pdfPrinter :: println
fun main(args: Array<String>) {
//包級函數引用
args.forEach(::println)
//類引用
val helloWorld = Hello::world
// args.filter(String::isNotEmpty)
//調用者引用方法
args.forEach(PdfPrinter()::println)
//Demo1
println(demo("k",::getRest))
//Demo2
demo2(Hello()::world)
}
class Hello {
fun world(){
println("123")
}
}
class PdfPrinter {
fun println(any: Any) {
kotlin.io.println(any)
}
}
fun getRest(s1:String):String="Petterp $s1"
fun demo(a: String, method: (s1: String) -> String): String = method(a)
fun demo2(methoud2:()->Unit)=methoud2()
複製代碼
forEach (一般用於遍歷集合)
val list = listOf(1, 3, 4, 5, 6, 7, 8)
val newList=ArrayList<Int>()
list.forEach {
newList.add(it*2)
}
newList.forEach(::println)
複製代碼
map (用於集合的映射,還能夠用於集合轉換)
val list = listOf(1, 3, 4, 5, 6, 7, 8)
val newList = list.map { it * 2 + 3 }
newList.forEach(::println)
複製代碼
flatmap (用於吧集合的集合扁平化成集合,換能夠結合map進行一些變換)
fun main() {
.flatMap { intRange ->
intRange.map { intElement ->
"No.$intElement"
}
}.forEach(::println)
//簡寫
list.flatMap {
it.map { "No.$it" }
}.forEach(::println)
}
複製代碼
fold (用於求和並加上一個初始值由於fold不一樣於map,fold對初始值沒有嚴格限制,所以fold還能夠進行類型變換)
fun main() {
//遍歷0..6的階乘
(0..6).map(::factorial).forEach(::println)
//加初始值5 結果: 879
println((0..6).map(::factorial).fold(5){acc, i ->acc+i})
//加上psotion
println((0..6).map(::factorial).foldIndexed(StringBuilder()){postion,acc, i ->acc.append("$i-$postion,")})
//更改返回類型 結果 1,1,2,6,24,120,720,
println((0..6).map(::factorial).fold(StringBuilder()){acc, i -> acc.append("$i,") })
//倒序
println((0..6).map(::factorial).foldRight(StringBuilder()){i,acc -> acc.append(i).append(",") })
//字符串鏈接
println((0..6).joinToString(","))
}
//求階乘
fun factorial(n:Int):Int{
if (n==0){
return 1
}
return (1..n).reduce { acc, i -> acc*i}
}
複製代碼
reduce (求和)
fun main() {
val list = listOf(
1..20,
6..100,
200..220
)
//求和 結果:9655
println(list.flatMap { it }.reduce { acc, i -> acc + i })
}
複製代碼
filter(用於過濾,若是傳入的表達式值爲true,就保留)
val list = listOf(
1,3,2
)
//傳入的表達式值爲true就保留
println(list.filter { it%2==0 })
//知足條件的保留,並有他們的位置
println(list.filterIndexed{index, i -> i%2==1 })
複製代碼
take (一般爲帶有條件的循環遍歷)
val list = listOf(
1,3,2
)
//從第一個元素開始,只返回符合的元素,遇到不符合中止
println(list.takeWhile { it%2==1 })
//從最後一個元素開始,一直取到不符合的就返回前面的元素
println(list.takeLastWhile { it%2==0})
//返回最後一個元素到指定元素位置的列表,不包含指定位置元素
println(list.takeLast(4))
//返回第一個一個元素到指定元素位置的列表,不包含指定位置元素
println(list.take(4))
//參數是個方法,返回值是一個布爾類型,爲真返回對象T,不然返回null
println(list.takeIf { it.size>6 })
複製代碼
let,apply,with,use(用於簡化代碼,use能夠簡化 colse,try/catch 統一使用模板)
data class Person(val name: String, val age: Int) {
fun work() {
println("我是$name")
}
}
fun main() {
findPerson()?.let {
it.work()
println(it.age)
}
//apply至關於一個擴展方法
findPerson()?.apply {
work()
println(age)
}
BufferedReader(FileReader("A.txt")).use {
var line:String?
while (true){
line=it.readLine()?:break
println(line)
}
}
// val br=BufferedReader(FileReader("hello.txt")).readLine()
}
fun findPerson(): Person? {
return null
}
複製代碼
尾遞歸指的是函數調用本身以後沒有任何操做,也就是一個函數中全部遞歸形式的調用都出如今函數的末尾。尾遞歸可使用 tailrec 關鍵字優化
遞歸的一種特殊形式
調用自身後無其餘操做
talrec關鍵字 提示編譯器尾遞歸優化
尾遞歸能夠直接轉換爲迭代
data class ListNode(val value:Int,var next:ListNode?=null)
tailrec fun findListNode(head:ListNode?,value:Int):ListNode?{
head?:return null
if (head.value==value) return head
return findListNode(head.next,value)
}
fun main() {
val Max_Node_Court=1000000
val head=ListNode(0)
var p=head
for (i in 1..Max_Node_Court){
p.next= ListNode(i)
p=p.next!!
}
println(findListNode(head,Max_Node_Court-2)?.value)
}
fun factorial(n:Long):Long{
return n* factorial(n-1)
}
data class TreeNode(val value:Int){
var left:TreeNode?=null
var right:TreeNode?=null
}
fun findTreNode(root:TreeNode?,value: Int):TreeNode?{
root?:return null
if (root.value==value) return root
return findTreNode(root.left,value)?:return findTreNode(root.right,value)
}
複製代碼
函數運行的環境
它持有函數運行的狀態
函數內部能夠定義函數
函數內部也能夠定義類
val String="HelloWord"
//Lambda 返回一個無參函數
fun makeFun():() -> Unit{
var count=0
return fun (){
println(++count)
}
}
fun main(args: Array<String>) {
// val x= makeFun()
// x()
// x()
val add5= add(5)
println(add5(2))
for (i in fibonac()){
if (i>100) break
println(i)
}
}
fun fibonac():Iterable<Long>{
var first=0L
var second=1L
return Iterable {
object :LongIterator(){
override fun hasNext()=true
override fun nextLong(): Long {
val result=second
second+=first
first=second-first
return result
}
}
}
}
//Demo2
//簡寫
fun add(x:Int)=fun (y:Int)=x+y
//完整
fun add2(x: Int):(Int)->Int{
return fun (y:Int):Int{
return x+y
}
}
複製代碼
f(g(x))
val add5={i:Int -> i+5} //f(g(x))
val mulyiplyBy2={i:Int ->i*2} // m(x)=f(g(x))
fun main(args: Array<String>) {
println(mulyiplyBy2(add5(8))) //(5+8)*2
val k= add5 andThen mulyiplyBy2
println(k(8))
}
/*P1,P2 表示參數值,R表示返回值 addThen爲擴展方法 infix中綴表達式 Function<P1,P2> p1爲參數類型,P2爲返回值類型 */
infix fun <P1,P2,R > Function1<P1,P2>.andThen(function: Function1<P2,R>):Function1<P1,R>{
return fun (p1:P1):R{
//返回了一個函數,函數裏面又把function這個參數調用了一遍
//調用的時候又把本身調用了一遍,把本身的返回值傳給function
return function.invoke(this.invoke(p1))
}
}
infix fun <P1,P2,R> Function1<P1,P2>.compose(function: Function1<P2,R>):Function1<P1,R>{
return fun (p1:P1):R
{
return function.invoke(this.invoke(p1))
}
}
複製代碼
Currying 簡單來講就是多元函數變換成一元函數調用鏈
//由多參數變換爲單參數的變化
fun Test(a: Int): (String) -> (Int) -> Boolean {
return fun(b: String): (c: Int) -> Boolean {
return fun(c: Int): Boolean {
return true
}
}
}
fun log(tag: String, target: OutputStream, message: Any?) {
target.write("[$tag] $message\n".toByteArray())
}
//柯里化
fun log2(tag: String)
= fun(target: OutputStream)
= fun(message: Any?)
= target.write("[$tag] $message\n".toByteArray())
fun main() {
log("benny",System.out,"Heelo2wor")
log2("Petterp")(System.out)("Demo")
::log.curried()("Petterp")(System.out)("Demo")
}
fun <P1,P2,P3,R> Function3<P1,P2,P3,R>.curried()
=fun(p1:P1)=fun (p2:P2)=fun (p3:P3)=this(p1,p2,p3)
複製代碼
傳入部分參數獲得的新函數
對於某些傳值比較固定的參數,偏函數能夠將其綁定,而後生成新的函數,而新的函數只須要給除已綁定的參數以外的參數傳值,固然你也能夠視同 默認參數+具名參數 的方式來實現參數的固定,若是須要固定的參數在中間,雖說能夠經過具名參數來解決,可是很尷尬,由於必須使用一大推具名參數,所以偏函數就誕生了
val test=fun(name:String,love:String):String{
return "${name}愛$love"
}
fun <P1,P2,R> Function2<P1,P2,R>.partial1(p1:P1)=fun(p2:P2)=this(p1,p2)
fun <P1,P2,R> Function2<P1,P2,R>.partial2(p2:P2)=fun (p1:P1)=this(p1,p2)
fun main() {
val t= test.partial1("Petterp")
println(t("寫Bug"))
val t2= test.partial2("改Bug")
println(t2("Petterp"))
}
複製代碼
空安全類型
Kotlin空安全類型原理
Kotlin在編譯的時候,會增長一個函數調用,會對參數類型,返回值類型進行是否爲null的檢查
複製代碼
平臺類型 PlatFromType
由於Java裏面並無空安全類型,因此可能會出現平臺類型的問題,這時候就須要咱們開發者本身明白鬚要使用的參數是否能夠爲null
複製代碼
@Nullable 和 @NotNull
在開發Java代碼的時候,能夠經過註解的方式來彌補這一點
複製代碼
幾類函數的調用
包級函數:靜態方法
在java裏並無這種函數,它在編譯的時候,會爲Kotlin生成一個類,這個類包含了全部包級函數,在java看來,這些都只是靜態方法,因此在java調用的時候,按照靜態按方法調用便可
複製代碼
擴展方法:帶 Receiver 的靜態方法
擴展方法只是增長了一個 Receiver 做爲參數
複製代碼
運算符重載:帶Receiver 的對應名稱的靜態方法
經常使用註解的使用
@JvmField : 將屬性編譯爲 JAVA變量
@JvmStataic :將對象的方法編譯成 Java靜態方法
@JvmOverloads : 默認參數生成重載方法
若是一個參數帶有默認參數,Java實際是看不見的
複製代碼
@file: JvmName : 指定Kotlin 文件編譯後的類名
NoArg 與 AllOpen
NoArg 爲被標註的類生成無參構造
支持Jpa註解,如 @Entity
複製代碼
AllOpen 爲被標註的類去掉final,容許被繼承
支持 Spring註解,如 @Component
複製代碼
支持自定義註解類型,例如 @Poko
泛型
通配符 Kotlin 的 * 對應java的 ?
協變與逆變 out/in
ArrayList<out String>
複製代碼
沒有Raw 類型
java 的List-> Kotlin的List<*>
複製代碼
//定義一個接口
interface SimpleInter {
fun test()
val number: Int
}
複製代碼
//open表示容許繼承,在Kotlin中,類和方法之間默認不容許繼承和重寫(不包括抽象類)
open class SimpleClass{
open fun put()
}
複製代碼
//實現接口中的參數
class Test1(override val number: Int) :SimpleClass(),SimpleInter{
//接口方法
override fun test() {
}
//重寫父類方法
override fun openTest() {
super.openTest()
}
}
複製代碼
//在kotlin中,默認會爲參數添加set,get方法,若是須要自定義,按照如下方式寫便可
class ClassTest1(override val number: Int) : SimpleInter {
override fun test() {
}
var money = number
get() {
return field
}
set(value) {
field = value
}
}
複製代碼
val number=Test1::number
//屬性調用
val classTest=ClassTest1(123)
val money=classTest::money
money.set(456)
複製代碼
class Book{
}
//定義Book類的擴展方法
fun Book.noBook(){
}
//也能夠定義擴展屬性
// Backing Field
//擴展成員變量沒法存儲狀態,由於不存在field
var Book.money: Int
get() {
return this.saveInt
}
set(value) {
this.saveInt = value
}
//接口能夠定義變量
interface Guy {
var moneyLeft: Double
get() {
return 0.0
}
set(value) {
}
}
複製代碼
class Book{
}
fun main() {
val book=Book()
if (book is Book){
//安全的類型轉換
println((book as? Book)?.javaClass?.name)
}
}
複製代碼
var b: String = "asdasd"
val c: Int = 15
複製代碼
字符串比較,
==,===
== 比較內容是否相等
=== 比較對象是否相等
//it表明當前數組下標
var intArray = IntArray(3) { it +3}
//獲取長度
println(intArray.size)
//打印數組
println(intArray.contentToString())
//打印數組
val dd= arrayOf(1,2,3)
println("${dd[1]},${dd[2]}")
//按數組下標打印
val ss= intArrayOf(1,2,3,4);
for (i in ss.indices){
println(ss[i])
}
//數組遍歷
for (d in dd){
print(d)
}
//函數式寫法
dd.forEach { d->
println(d)
}
//若是使用默認it,則能夠省略 d->
dd.forEach { println(it)}
/* 判斷是否在一個數組內 */
if (5 in dd){
println("在數組dd內")
}
if (5 !in dd){
println("不在數組dd內")
}
複製代碼
//表示建立了一個1-10的閉區間
val intRange=1..10
val charRange='a'..'z'
val longRange=1L..1000L
//建立開區間
val intRangeExclusive=1 until 10 //[1,10)
//倒序區間
val intRangeReverse=10 downTo 1 //[10,9,...,1]
//區間添加步長
val intRange1=1..10 step 3 // [1,4,7,9,10]
//打印區間
println(intRange1.joinToString())
複製代碼
//不可變List,不可添加或刪除
val intList: List<Int> = listOf(1,2,3)
//可變List
val intList2: MutableList<Int> = mutableListOf(1, 2, 3, 4)
//不可變map
val map:Map<String,Any> = mapOf("name" to "petterp","ts" to 123)
val mutilMap:Map<String,Any> = mutableMapOf("name" to "petterp","Ts" to 123)
//直接建立Java中的List
val intList = ArrayList<Int>()
val stringList = ArrayList<String>()
val intList = ArrayList<Int>()
//List的寫入 等價於 add
for (i in 0..10){
intList+=i
}
//刪除某個元素,等價於 remove
intList.remove(100)
//修改某個元素的值 相似於 set(0,101)
intList[0]=101
val maps = HashMap<String, String>(10)
//添加某個元素
maps+=("key1" to "test")
println(maps.toString())
//修改某個元素
maps["key1"]="petterp"
println(maps.toString())
複製代碼
Any 至關於Object
若是咱們查看 Kotlin中的 ArrayList,會發現
其中 Typealias 至關與爲類型起新名稱
咱們在上面Map中用到了 」「 to ""
Var pair=""
val map = HashMap<String, String>()
val pair = "key" to "petterp"
val pair2 = Pair("Hello", "Petterp")
//獲取鍵
val key = pair.first
//獲取值
val second = pair.second
//puts如map
map += pair
map += pair2
println("$key , $second")
複製代碼
用於將一個對象解構成多個變量
fun main() {
val map = HashMap<String, Int>()
val kotlinBook = "Kotlin核心技術" to 88
val AndroidBook = Pair("Android藝術探索", 66)
println("${kotlinBook.first} , ${kotlinBook.second}")
map += kotlinBook
map += AndroidBook println("解構開始\n\n") /*解構聲明*/ //參考https://www.kotlincn.net/docs/reference/multi-declarations.html //將一個對象解構爲多個變量 println("解構對象") val (x, y) = Book("Kotlin核心技術", 88)
println(x)
println(y)
println("\n 遍歷map")
//使用解構遍歷map,多是最好的方式
for ((name, value) in map) {
println("$name , $value")
}
println("\n 忽略某些解構")
//若是你不想解構某些變量,則經過 _ 標誌
for ((name, _) in map)
println(name)
println("\n lambda中使用解構")
//使用解構返回一個新的map,key不變,返回的只是value的改變
val maps = map.mapValues { (name, value) -> "5" }
println(map.toString())
println(maps.toString())
println("\n 函數中使用解構")
//從函數中返回兩個變量
val (name, money) = funcation()
println("$name , $money")
//從函數中返回兩個變量
}
data class Book(var name: String, var money: Int) fun funcation(): no1.Book {
println("經歷了一波操做")
return no1.Book("Android藝術探索", 99)
}
複製代碼
Kotlin核心技術 , 88
解構開始
解構對象
Kotlin核心技術
88
遍歷map
Kotlin核心技術 , 88
Android藝術探索 , 66
忽略某些解構
Kotlin核心技術
Android藝術探索
lambda中使用解構
{Kotlin核心技術=88, Android藝術探索=66}
{Kotlin核心技術=5, Android藝術探索=5}
函數中使用解構
經歷了一波操做
Android藝術探索 , 99
複製代碼
在你的判斷邏輯中,充當永遠不可能調用的哪一項,好比你有一個when的選擇語句,就可使用Nothing做爲你的else返回。
先看一下Nothing是什麼
/** * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example, * if a function has the return type of Nothing, it means that it never returns (always throws an exception). */
//*沒有實例。您可使用Nothing來表示「一個永不存在的值」:例如,*若是函數的返回類型爲Nothing,則表示該函數永不返回(老是引起異常)。
public class Nothing private constructor()
複製代碼
有下面這樣一段代碼:
fun test(type: String) =
when (type) {
"key1" -> "條件1"
"key2" -> "條件2"
else -> caseDefault()
}
/** 一個報錯的邏輯,爲了便於編譯器識別,返回類型依然爲String * 若是是其餘狀況,相應返回類型須要更改方可便於編譯器,不然相應的異常每一個方法都須要手動添加 */
private fun caseDefault(): String {
throw RuntimeException("123")
}
複製代碼
使用 Nothing 優化這段代碼
fun test(type: String) =
when (type) {
"key1" -> "條件1"
"key2" -> "條件2"
else -> doNothing()
}
private fun doNothing(message: String="Nothing is all"): Nothing {
throw RuntimeException(message)
}
複製代碼
fun main() {
//函數引用的幾種方法
val x = Test::pp
val x1: (Test, String) -> String = Test::pp
val x2: (Test, String) -> String = x
val x3: Function2<Test, String, String> = Test::pp
}
class Test {
fun pp(name: String): String {
return name
}
}
複製代碼
fun main(){
val x1: (Test, String) -> String = Test::pp
println(Test().pp2(x1, 123))
}
class Test(){
fun pp(name: String): String {
return name
}
fun pp2(name: (Test, String) -> String, value: Int): String {
return "${name(Test(), "132")} , $value"
}
}
複製代碼
fun main(){
//默認參數
default("Android")
//若是你的默認參數爲第一個,此時就須要聲明顯示類型
default(name = "Android")
}
fun default(name: String, money: Int = 9) {
}
複製代碼
fun main(){
//變長參數
channels("Asd", "Asd")
}
fun channels(vararg args: String) {
println(args.joinToString())
}
複製代碼
fun main(){
var (a, b, c) = test1()
println("$a , $b ,$c")
}
fun test1(): Triple<Int, String, String> {
return Triple(123, "asd", "asdfa");
}
複製代碼
fun main() {
val res: String = readLine()!!
if (res.isEmpty()) {
throw RuntimeException("不可爲null")
}
val operations = mapOf(
"+" to ::plus,
"-" to ::minus,
"*" to ::times,
"/" to ::div
)
when {
"+" in res -> {
val (a, b) = res.split("+")
println(operations["+"]?.invoke(a.toInt(), b.toInt()))
}
"-" in res -> {
}
"*" in res -> {
}
"/" in res -> {
}
}
}
fun plus(a: Int, b: Int): Int {
return a + b
}
fun minus(a: Int, b: Int): Int {
return a + b
}
fun times(a: Int, b: Int): Int {
return a + b
}
fun div(a: Int, b: Int): Int {
return a + b
}
複製代碼
fun main() {
Kang::name
Kang::money //報錯
}
//加了var或者val 就至關於成員變量,全局可見,不然就只能在構造器(init塊)內可見,相似於局部變量
class Kang(var name:String,money:Int)
複製代碼
init會在最後合成爲一個init塊,會與構造方法一塊兒執行
class Kang(var name:String,money:Int){
init {
this.name=money.toString()
}
init {
println()
}
init {
println("我是Petterp")
}
}
複製代碼
在Java裏面,一個類能夠有多個構造器,也就意味着咱們初始化這個類的方法有不少種,這也就意味着咱們有不少生命了的變量沒法被使用到。
class Kang(override var name:String, money:Int):Test(name){
init {
this.name=money.toString()
}
init {
println()
}
init {
println("我是Petterp")
}
fun void(){
}
//副構造器,同時調用了主構造器
constructor(name: String):this(name,123){
this.name=name
}
}
複製代碼
若是未定義主構造器,則能夠直接調用其餘副構造器,和java基本沒有什麼不一樣.不過這種寫法並不推薦
class Test3{
constructor(name:String){
}
constructor(money:Int):this("123"){
}
}
複製代碼
Kotlin推薦以主構造器+默認參數的形式去寫。若是要用在Java中,則加上 @JvmOverloads
class Test3 constructor(){
constructor(name:String){
}
@JvmOverloads
constructor(money:Int):this("123"){
}
}
複製代碼
可見性類型 | Java | Kotlin |
---|---|---|
public | 公開 | 與java相同,默認就是public |
internal | x | 模塊內可見 |
default | 包內可見,默認 | x |
protected | 包內及子類可見 | 類內及子類可見 |
private | 類內可見 | 類或文件內可見 |
在Java中能夠直接訪問到 internal ,由於Java並不認識Kotlin中的 internal。以下,兩個模塊中
Kotlin:
Java:
若是咱們想避免Java直接訪問到咱們的代碼,能夠加入如下小技巧,這樣當Java調用時就會因不規範而報錯。
//
class Person(var age: Int, var name: String) {
var fitstName: String = ""
private set(value) {
field = name
}
}
複製代碼
頂級聲明指文件內直接定義的屬性,函數,類等
頂級聲明不支持 protected
頂級聲明被 private 修飾表示文件內部可見
有以下三種方式:
private val rvMainActivity by lazy {
findViewById<RecyclerView>(R.id.rv_main)
}
複製代碼
private lateinit var recyclerView:RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView=findViewById(R.id.rv_main)
}
複製代碼
private var recyclerView:RecyclerView?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView=findViewById(R.id.rv_main)
recyclerView?.adapter=RecyclerCustomAdapter(arrayListOf("123","234"))
}
複製代碼
用於監控屬性值發生變動,相似一個觀察者,當屬性值被修改後往外部拋出一個變動的回調。
//使用state變量代理StateManager,從而屬性更改時實現監聽
class StateManager{
var state:Int by Delegates.observable(0){
property, oldValue, newValue ->
println("$oldValue->$newValue")
}
}
fun main() {
var stateManager = StateManager()
stateManager.state=4
stateManager.state=5
}
複製代碼
fun main(){
//屬性x將它的訪問器邏輯委託給了X對象
var x:Int by X()
}
//遵循getValue,setValue,若是是val,只支持getValue
class X{
operator fun getValue(nothing: Nothing?, property: KProperty<*>): Int {
return 2
}
operator fun setValue(nothing: Nothing?, property: KProperty<*>, i: Int) {
}
}
複製代碼
須要在使用前初始化
//返回具備 非Null參數的委託
var name:String by Delegates.notNull()
複製代碼
用於在屬性改變發生是通知外部變化,在裏面能夠作一些改變,返回一個狀態,若是知足條件返回true,不然false
var vetoable: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
println("old-$oldValue---new-$newValue")
return@vetoable newValue == 123
}
複製代碼
用於在屬性發生變化時調用指定的回調函數
var observable:Int by Delegates.observable(0){
property, oldValue, newValue ->
}
複製代碼
class PropertiesDelegate(private val path: String, private val defaultValu: String = "") {
private lateinit var url: URL
private val properties: Properties by lazy {
val prop = Properties()
url = try {
javaClass.getResourceAsStream(path).use {
prop.load(it)
}
javaClass.getResource(path)!!
} catch (e: Exception) {
try {
ClassLoader.getSystemClassLoader().getResourceAsStream(path).use {
prop.load(it)
}
ClassLoader.getSystemClassLoader().getResource(path)!!
} catch (e: Exception) {
FileInputStream(path).use {
prop.load(it)
}
URL("file://${File(path).canonicalPath}")
}
}
prop
}
operator fun getValue(thisRef: Any?, kProperty: KProperty<*>): String {
return properties.getProperty(kProperty.name, defaultValu)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
properties.setProperty(property.name, value)
File(url.toURI()).outputStream().use {
properties.store(it, "petterp")
}
}
}
abstract class AbsProperties(path: String) {
protected val prop = PropertiesDelegate(path)
}
class Config : AbsProperties("Config.properties") {
var name by prop
}
fun main() {
val config=Config()
println(config.name)
config.name="asdasd"
println(config.name)
}
複製代碼
//餓漢式單例
object Singleton{
}
複製代碼
便於Java調用
@JvmStatic 生成靜態方法
@JvmField 生成set,get
複製代碼
Kotlin中沒有static變量,因此使用伴生對象代替靜態變量。使用前須要帶上 相應註解
class Test{
companion object{
fun of(){
}
}
}
複製代碼
class Outher{
//非靜態內部類
inner class Inner{
}
//靜態內部類
class staticInner{
}
}
複製代碼
class Outher {
//非靜態內部類
inner class Inner {
}
//靜態內部類
class staticInner {
object OutherObject {
var x=5
}
}
}
fun main() {
Outher.staticInner.OutherObject.x
}
複製代碼
fun main() {
//可繼承父類或實現多個接口
//類型-交叉類型 ClassPath&Runnable(猜想)
object : ClassPath(), Runnable {
override fun run() {
}
override fun test() {
}
}
}
abstract class ClassPath {
abstract fun test()
}
複製代碼
data class Book(val id:Int,val money:Double){
}
複製代碼
JavaBean | data class | |
---|---|---|
構造方法 | 默認無參構造 | 屬性做爲參數 |
字段 | 字段私有,Getter/Setter公開 | 屬性 |
繼承性 | 可繼承也可被繼承 | 不可被繼承 |
Component | 無 | 相等性,解構等 |
enum class State{
A,B
}
enum class States(val id:Int){
Idle(0),Busy(1)
}
//枚舉實現接口
enum class ClassPath : Runnable {
IDLE,BUSY;
override fun run() {
}
}
//爲每個枚舉實現接口方法
enum class EnumOffer : Runnable {
idle {
override fun run() {
}
},
busy{
override fun run() {
}
}
override fun run() {
}
}
複製代碼
fun State.sout() {
println("擴展")
}
複製代碼
enum class State {
A, B
}
fun main() {
val s = State.B
if (s>State.A) println("ok")
}
複製代碼
enum class State {
A, B,C,D,E
}
fun main() {
val s = State.B
if (s in State.A..State.C) println("ok")
}
複製代碼
簡單來講,密封類至關於一類事物的具體子分類,有明確的類型區別,子類有具體個數。在when表達式中,對密封類有優化效果
sealed class Book(val name: String)
class Android(name: String) : Book(name)
class Java(name: String, var money: Int) : Book(name)
fun main() {
list(Android("Kotlin"))
list(Java("Javaxx", 99))
}
//在條件所有知足的狀況下,無需else
fun list(book: Book) = when (book) {
is Android -> println(book.name)
is Java -> println("${book.name} -- ${book.money}")
}
複製代碼
密封類 | 枚舉類 | |
---|---|---|
狀態實現 | 子類繼承 | 類實例化 |
狀態可數 | 子類可數 | 實例可數 |
狀態差別 | 類型差別 | 值差別 |
inline class Unit constructor(internal val data: Int) : Comparable<Unit> {
override fun compareTo(other: Unit): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
複製代碼