簡介
Kotlin 是一個基于 JVM 的新的編程語言浅蚪,由 JetBrains 開發(fā)拘领。于2010年首次推出意乓,次年開源。它與Java 100%互通约素,并具備諸多Java尚不支持的新特性
設計目標
創(chuàng)建一種兼容Java的語言
讓它比Java更安全届良,能夠靜態(tài)檢測常見的陷阱。如引用空指針
讓它比Java更簡潔 如高階函數(shù)圣猎、擴展函數(shù)等
變量士葫、方法、類的定義
變量常量定義
可變變量定義:
var 關(guān)鍵字
var <標識符> : <類型> = <初始化值>
var age :Int =1
var age =1
不可變變量定義:
val 關(guān)鍵字送悔,只能賦值一次的變量(類似Java中final修飾的變量)
val <標識符> : <類型> = <初始化值>
方法定義
函數(shù)定義使用關(guān)鍵字 fun慢显,參數(shù)格式為:參數(shù) : 類型
fun sum(a: Int, b: Int): Int { // Int 參數(shù),返回值 Int
return a + b
}
表達式類型確定的值作為函數(shù)體放祟,返回類型自動推斷
fun sum(a: Int, b: Int) = a + b
fun equals(a: Int, b: Int) = false
無返回值的函數(shù)(類似Java中的void):
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
// 如果是返回 Unit類型鳍怨,則可以省略:
fun printSum(a: Int, b: Int) {
print(a + b)
}
匿名函數(shù)
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 輸出 3
}
類的定義和實例
Kotlin 中使用關(guān)鍵字 class 聲明類呻右,后面緊跟類名:
class Foo { // 類名為 Foo
// 大括號內(nèi)是類體構(gòu)成
}
我們也可以定義一個空類:
class Foo
類的屬性可以用關(guān)鍵字 var 聲明為可變的跪妥,否則使用只讀關(guān)鍵字 val 聲明為不可變。
class Foo {
var name: String = ……
var url: String = ……
var city: String = ……
}
屬性可以放到構(gòu)造函數(shù)里面
class Foo( var name: String , var url: String , var city: String)
數(shù)據(jù)類data class 自動獲取需要的getters声滥,setters眉撵,equals(),hashcode()落塑,toString()和copy()函數(shù)
data class Person(var name: String,var age: Int,var height: Float = 1.8f)
等同的java class
public final class Person {
private String name;
private int age;
private float height;
public Person(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
this.height = 1.8f;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
if (Float.compare(person.height, height) != 0) return false;
return name != null ? name.equals(person.name) : person.name == null
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
result = 31 * result + (height != +0.0f ? Float.floatToIntBits(height) : 0);
return result;
}
}
實例化一個對象纽疟,不需要new關(guān)鍵字
var foo = Foo()
字符串模板
$ 表示一個變量名或者變量值
$varName 表示變量值
${varName.fun()} 表示變量的方法返回值:
如
val person = Person("小明",12)
val string = "person=[${person.name},${person.age}]"
對比java
Person person = new Person("小明",12);
String string = "person="+"["+person.getName()+","+person.getAge()+"]";
NULL檢查機制
Kotlin 在變量定義的時候就指定是否可空
var name:String ="小明"
var city:String?=null//用?表示可空
String name ="小明"
String city =null
在一個方法中
var length =city.length()
在編譯期間就會報錯,這樣寫就不會編譯出錯
var length =city?.length()
如果用java來寫
int length =city.length()
在編譯期間是無法感知異常的,有可能在項目運行時就crash了
類型檢測及自動類型轉(zhuǎn)換
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做過類型判斷以后憾赁,obj會被系統(tǒng)自動轉(zhuǎn)換為String類型
return obj.length
}
return null
}
還可以這樣
fun getStringLength(obj: Any): Int? {
// 在 `&&` 運算符的右側(cè), `obj` 的類型會被自動轉(zhuǎn)換為 `String`
if (obj is String && obj.length > 0)
return obj.length
return null
}
對比java 的代碼污朽,需要將數(shù)據(jù)類型強制轉(zhuǎn)換
public Integer getStringLength(Object object) {
if (object instanceof String) {
//需要強轉(zhuǎn)一下
return ((String) object).length();
}
return null;
}
擴展函數(shù)
相信大家項目中有很多的util類,如StringUtils
Kotlin有一個聰明的解決方案 龙考, 擴展函數(shù) 蟆肆,幫助你擺脫所有的util類一勞永逸
fun Context.toast(text: String) = Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
在所有的Context子類中可以直接調(diào)用toast("somewords")
基本數(shù)據(jù)類型也可以這樣
fun Int.dp2px():Int {
return ......
}
代碼中可以直接調(diào)用
var px =12.dp2px()
默認參數(shù)
fun Date.format(format: String = "yyyy年MM月dd日 HH:mm"): String {
return SimpleDateFormat(format, Locale.CHINA).format(this)
}
可以在代碼中這樣使用
var dateString =Date().format()
也可以
var dateString =Date().format(“yyyy-MM-dd HH:mm”)
高階函數(shù)
什么是高階函數(shù)
高階函數(shù)就是以另一個函數(shù)作為參數(shù)或返回值的函數(shù)矾睦,Kotlin可以以lambda或參數(shù)引用作為參數(shù)或返回值,所以炎功,任何以lambda或函數(shù)引用作為參數(shù)或返回值的都是高階函數(shù)
要使用Kotlin的高階函數(shù)就必須遵循它的函數(shù)類型
先來看一個簡單的例子枚冗,這是一個簡單的函數(shù)類型申明
val sum = { x: Int, y: Int -> x + y }
之所以能這么寫得益于Kotlin的類型推導,它的顯示寫法是這樣的:
var sum:(Int,Int)-> Int = {x , y-> x + y}
高階函數(shù)的使用
將函數(shù)類型作為參數(shù)蛇损,直接看代碼
fun getNumResult(result: (Int, Int) -> Int): Int {
return result(1,2)
}
//調(diào)用
var value = getNumResult({ a, b -> a + b })
//方法里面最后一個參數(shù)是函數(shù)的時候可以省略
var value = getNumResult{ a, b -> a + b }
==> value = 3
var value = getNumResult{ a, b -> a * b }
==> value = 2
android 中常用的設置點擊事件就可以簡單的寫成
view.setOnClickListener { v ->
{
//傲尬隆!我被點擊了
}
}
//淤齐,當參數(shù)只有一個lambda參數(shù)的時候 可以省略
view.setOnClickListener {
//肮赡摇!我被點擊了
}
正常的java代碼
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//按膊!毁涉!我被點擊了
}
});
高階函數(shù)在集合中的應用
val list = arrayListOf("aaa", "bb", "c","b")
list.sortBy {
it.length
}
val find = list.find {
it.length == 1
}
val indexOfFirst = list.indexOfFirst {
it.startsWith("b")
}
val filter = list.filter {
it.length == 1
}
等等
運算符重載和迭代器重載
運算符重載
data class Area(var width: Int, var height: Int) {
operator fun plus(other: Area): Area {
return Area(this.width + other.width, this.height + other.height)
}
operator fun compareTo(other: Area): Int {
return this.width * this.height - other.width * other.height
}
}
val area1 = Area(6, 3)
val area2 = Area(4, 5)
print(area1 + area2)
==> Area(width=10, height=8)
print(area1 > area2)
==>false
常用的重載 加減乘除、compareTo等
迭代器重載
迭代器(iterator)是java中我們非常熟悉的東西了锈死,數(shù)據(jù)結(jié)構(gòu)如List和Set都內(nèi)置了迭代器贫堰,我們可以通過它提供的方法類遍歷訪問一個聚合對象中的各個元素
data class Book(val name: String)
class Bookcase(val books: List<Book>)
operator fun Bookcase.iterator(): Iterator<Book> = books.iterator()
重載iterator方法后的類可以通過以下方法遍歷
val list =ArrayList<Book>()
val case = Bookcase(list)
for (book in case) {
...
}
單例
java中單例的定義
public class DataCenter {
private static DataCenter sInstance;
private DataCenter() {
}
public static DataCenter getInstance() {
if (sInstance == null) {
sInstance = new DataCenter();
}
return sInstance;
}
}
Kotlin 單例實現(xiàn)只需要關(guān)鍵字object
object DataCenter {
}
object 全局聲明的對象只有一個
內(nèi)聯(lián)函數(shù)
當我們使用lambda
表達式時,它會被正常地編譯成匿名類待牵。這表示每調(diào)用一次lambda
表達式其屏,一個額外的類就會被創(chuàng)建,并且如果lambda
捕捉了某個變量缨该,那么每次調(diào)用的時候都會創(chuàng)建一個新的對象偎行,這會帶來運行時的額外開銷,導致使用lambda
比使用一個直接執(zhí)行相同代碼的函數(shù)效率更低贰拿。
如果使用inline
修飾符標記一個函數(shù)蛤袒,在函數(shù)被調(diào)用的時候編譯器并不會生成函數(shù)調(diào)用的代碼,而是 使用函數(shù)實現(xiàn)的真實代碼替換每一次的函數(shù)調(diào)用膨更。
內(nèi)聯(lián)函數(shù)如何運作
當一個函數(shù)被聲明為inline
時妙真,它的函數(shù)體是內(nèi)聯(lián)的,也就是說荚守,函數(shù)體會被直接替換到函數(shù)被調(diào)用地方珍德,下面我們來看一個簡單的例子,下面是我們定義的一個內(nèi)聯(lián)的函數(shù):
fun inlineFunc(prefix : String, action : () -> Unit) {
println("call before $prefix")
action()
println("call after $prefix")
}
我們用如下的方法來使用這個內(nèi)聯(lián)函數(shù):
fun main(args: Array<String>) {
inlineFunc("inlineFunc") {
println("HaHa")
}
}
運行結(jié)果為:
>> call before inlineFunc
>> HaHa
>> call after inlineFunc
最終它會被編譯成下面的字節(jié)碼:
fun main(args: Array<String>) {
println("call before $prefix")
println("HaHa")
println("call after $prefix")
}
lambda
表達式和inlineFunc
的實現(xiàn)部分都被內(nèi)聯(lián)了矗漾,由lambda
生成的字節(jié)碼成了函數(shù)調(diào)用者定義的一部分锈候,而不是被包含在一個實現(xiàn)了函數(shù)接口的匿名類中。
內(nèi)聯(lián)擴展函數(shù)之let
let函數(shù)通常作用就是避免寫一些判斷null的操作敞贡。
let函數(shù)的使用的一般結(jié)構(gòu)
object.let{
it.todo()//在函數(shù)體內(nèi)使用it替代object對象去訪問其公有的屬性和方法
...
}
//另一種用途 判斷object為null的操作
object?.let{//表示object不為null的條件下泵琳,才會去執(zhí)行l(wèi)et函數(shù)體
it.todo()
}
內(nèi)聯(lián)函數(shù)之with
with函數(shù)使用的一般結(jié)構(gòu)
with(object){
//todo
}
適用于調(diào)用同一個類的多個方法或成員時,可以省去類名重復,直接調(diào)用類的方法即可
val person =Person()
with(viewHolder){
tvName.setText(person.name)
tvAge.setText(person.age)
}
內(nèi)聯(lián)擴展函數(shù)之a(chǎn)pply
apply函數(shù)使用的一般結(jié)構(gòu)
object.apply{
//todo
}
apply一般用于一個對象實例初始化的時候获列,需要對對象中的屬性進行賦值
val person =Person().apply{
age =12
name ="小明"
}
泛型及reified函數(shù)
reified 具體化的,對比java和kotlin中的實現(xiàn)
public static <T> T parseObject(String text, Class<T> cls) {
return JSON.parseObject(text, cls);
}
inline fun <reified T> parseObject(text: String?): T {
return JSON.parseObject(text, T::class.java)
}
結(jié)合上面的擴展函數(shù)還可以這樣
inline fun <reified T> String?.parseObject(): T {
return JSON.parseObject(this, T::class.java)
}
調(diào)用的時候
val testJson = "{\"age\":22,\"name\":\"小明\"}"
val person: Person = testJson.parseObject()
利用這種特性還可以實現(xiàn)一個函數(shù)返回不同的數(shù)據(jù)類型
inline fun <reified T> Int.times(): T? {
return when (T::class) {
Int::class -> (this * 2) as T
String::class -> ("$this$this") as T
else -> null
}
}
val stringValue: String = 12.times()
==> "1212"
val intValue: Int = 12.times()
==> 24
....