自Kotlin被google官方認(rèn)可之后回懦,Android界對(duì)Kotlin的熱度便持續(xù)上漲,在了解了它的優(yōu)勢(shì)后,作為一名與時(shí)俱進(jìn)的Android開發(fā)工程師癌蓖,是時(shí)候有必要來學(xué)習(xí)Kotlin了。
Kotlin有何優(yōu)勢(shì)婚肆?
- 代碼的大幅度精簡(jiǎn)
- 100% 兼容 Java 代碼
以上兩點(diǎn)是我認(rèn)為Kotlin的最大優(yōu)勢(shì)租副,目前java 的主流開發(fā)工具基本都支持了java轉(zhuǎn)Kotlin,因此轉(zhuǎn)換基本不需要什么成本较性,就算在使用過程中遇到坑用僧,完全可以用java來寫结胀,無隔閡的運(yùn)行,一開始不熟悉的時(shí)候還可以用java編寫后轉(zhuǎn)成Kotlin责循,不斷對(duì)比就會(huì)逐漸熟悉糟港。(ps:一個(gè)好的IDE對(duì)學(xué)習(xí)也是非常有幫助,在寫Kotlin的代碼時(shí)android studio 經(jīng)常會(huì)提醒我有簡(jiǎn)化的寫法)
Kotlin基礎(chǔ)語法
Kotlin在線學(xué)習(xí)工具
Kotlin 中文站
對(duì)比學(xué)習(xí)
kotlin中一切皆為對(duì)象 院仿,沒有像java一般的基本數(shù)據(jù)類型秸抚,數(shù)值類型為:Int, Float, Double等都是對(duì)象(類似java的包裝類);函數(shù)也是對(duì)象歹垫,可作為參數(shù)和返回值剥汤。
定義常量與變量
可變變量定義:var 關(guān)鍵字
var <標(biāo)識(shí)符> : <類型> = <初始化值>
不可變變量定義:val 關(guān)鍵字,只能賦值一次的變量(類似Java中final修飾的變量)
val <標(biāo)識(shí)符> : <類型> = <初始化值>
類型檢測(cè)及自動(dòng)類型轉(zhuǎn)換
我們可以使用 is 運(yùn)算符檢測(cè)一個(gè)表達(dá)式是否某類型的一個(gè)實(shí)例(類似于Java中的instanceof關(guān)鍵字)排惨。
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做過類型判斷以后吭敢,obj會(huì)被系統(tǒng)自動(dòng)轉(zhuǎn)換為String類型
return obj.length
}
}
- Kotlin中的Any = Java中的Object
類型強(qiáng)轉(zhuǎn)
類型強(qiáng)轉(zhuǎn)在Android中非常常見,如:
GridView gridView = (GridView) findViewById(R.id.grid_view);
而Kotlin中使用as關(guān)鍵詞:
val recyclerView = findViewById(R.id.recycler_view) as RecyclerView
字符串模板
$ 表示一個(gè)變量名或者變量值
$varName 表示變量值
${varName.fun()} 表示變量的方法返回值:
var a = 1
// 模板中的簡(jiǎn)單名稱:
val s1 = "a is $a"
a = 2
// 模板中的任意表達(dá)式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
static : companion object
java中static表示類相關(guān)的暮芭,生命周期于類相同鹿驼,Android中經(jīng)常會(huì)用來聲明一些static常量和工具類的static方法
public static final int A = 1;
public static final String STR = "abc";
public static int getInt() {
return A;
}
在Kotlin中則沒有static關(guān)鍵字,取而代之的是companion object (伴生對(duì)象)
companion object{
val A = 1 //val表示不可變辕宏,類似java的 final
val STR = "abc"
}
在Android中常見的utils類(只有靜態(tài)方法)可以變成這樣:
class Utils private constructor() {
init {
throw AssertionError()
}
companion object {
fun staticFun() {/*do someting*/
}
}
}
當(dāng)然畜晰,這是java思維下Kotlin轉(zhuǎn)變,實(shí)際上用Kotlin實(shí)現(xiàn)工具類還有其他的方法匾效,其一是使用object:
object Utils {
/** 獲得狀態(tài)欄的高度 */
@JvmStatic
fun getStatusHeight(context: Context): Int {
var statusHeight = -1
try {
val clazz = Class.forName("com.android.internal.R\$dimen")
val obj = clazz.newInstance()
val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
statusHeight = context.resources.getDimensionPixelSize(height)
} catch (e: Exception) {
e.printStackTrace()
}
return statusHeight
}
}
注意@JvmStatic
舷蟀,代表了在Java調(diào)用時(shí)也可以像static方式那樣使用:Utils.getStatusHeight()
,如果不加這個(gè)注解的話在java中調(diào)用Utils.INSTANCE.getStatusHeight()
面哼。
還有另一種方式頂層函數(shù)野宜,也是Kotlin實(shí)戰(zhàn)這本書中推薦的方式,不需要?jiǎng)?chuàng)建類魔策,而是直接創(chuàng)建一個(gè).kt文件定義函數(shù)(方法):
@file:JvmName("ScreenUtil")//定義java中的類名
package com.huburt.library.util
import android.content.Context
/**
* Created by hubert
*
* Created on 2017/11/2.
*/
fun getStatusHeight(context: Context): Int {
var statusHeight = -1
try {
val clazz = Class.forName("com.android.internal.R\$dimen")
val obj = clazz.newInstance()
val height = Integer.parseInt(clazz.getField("status_bar_height").get(obj).toString())
statusHeight = context.resources.getDimensionPixelSize(height)
} catch (e: Exception) {
e.printStackTrace()
}
return statusHeight
}
Kotlin中可以直接使用該方法匈子,會(huì)自動(dòng)導(dǎo)入import com.huburt.library.util.getStatusHeight
,java中使用還是會(huì)像使用static方法一樣ScreenUtil.getStatusHeight(this);
闯袒,如果沒有定義java類名@file:JvmName("ScreenUtil")
的話虎敦,會(huì)默認(rèn)使用.kt的文件名+Kt,如這里我是放在ScreenUtil.kt文件中政敢,使用就是ScreenUtilKt.getStatusHeight(this);
既然Kotlin沒有static關(guān)鍵字其徙,java中較多使用的
單例模式
class Singleton {
private Singleton() {
System.out.print("This is a singleton");
}
private static class Holder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
在Kotlin中也需要作出改變
class Singleton private constructor() {
init {
println("This ($this) is a singleton")
}
private object Holder {
val INSTANCE = Singleton()
}
companion object {
val instance: Singleton by lazy { Holder.INSTANCE }
}
}
構(gòu)造
可以看到Kotlin的constructor(構(gòu)造器)位置與java中不同。
Koltin 中的類可以有一個(gè) 主構(gòu)造器喷户,以及一個(gè)或多個(gè)次構(gòu)造器唾那,主構(gòu)造器是類頭部的一部分,位于類名稱之后:
class Person constructor(firstName: String) {}
如果主構(gòu)造器沒有任何注解褪尝,也沒有任何可見度修飾符闹获,那么constructor關(guān)鍵字可以省略期犬。
class Person(firstName: String) {}
主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中避诽,初始化代碼段使用 init 關(guān)鍵字作為前綴龟虎。
class Person constructor(firstName: String) {
init {
System.out.print("FirstName is $firstName")
}
}
注意:主構(gòu)造器的參數(shù)可以在初始化代碼段中使用,也可以在類主體n定義的屬性初始化代碼中使用沙庐。 一種簡(jiǎn)潔語法鲤妥,可以通過主構(gòu)造器來定義屬性并初始化屬性值(可以是var或val):
class People(val firstName: String, val lastName: String) {
//...
}
如果類有主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)都要轨功,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)旭斥。在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字:
class Person() {
var parent: Person? = null
constructor(parent: Person) : this() {
this.parent = parent
}
}
對(duì)象
Kotlin中創(chuàng)建對(duì)象不需要 new
,只需要調(diào)用構(gòu)造器如:var person = Person()
匿名內(nèi)部類
Android中對(duì)于匿名內(nèi)部類的使用也非常多容达,典型的setOnClickLsitener
,而在Kotlin中使用object關(guān)鍵字實(shí)現(xiàn)匿名內(nèi)部類:
button.setOnClickListener(object: OnClickListener {
override fun onClick(v: View?){}
})
javaBean
javaBean也是Android開發(fā)中非常常見的古涧,用于封裝數(shù)據(jù),這是一個(gè)基本的網(wǎng)絡(luò)結(jié)果實(shí)體類
public class BaseEntity<T> {
private int code;
private String message;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
用Kotlin去聲明的話:
class BaseEntity<T> {
var code: Int = 0
// set //默認(rèn)實(shí)現(xiàn)
// get
var message: String? = null
var data: T? = null
}
不要認(rèn)為這只是java中public聲明的屬性花盐,這些屬性都有默認(rèn)實(shí)現(xiàn)的getter/setter羡滑,如果不想某個(gè)屬性被修改(只讀)只需要在set前聲明 private
。
還有另外一種更簡(jiǎn)便的寫法算芯,Kotlin中定義了一種數(shù)據(jù)類,可以創(chuàng)建一個(gè)只包含數(shù)據(jù)的類柒昏,使用關(guān)鍵字為 data:
data class DataClassExample (val x: Int, val y: Int, val z: 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)接口)。
var message: String?
是否對(duì)這個(gè)?
感到奇怪意系。其實(shí)這是表示這個(gè)屬性可能為null泥耀,在使用的時(shí)候需要進(jìn)行處理。
NULL檢查機(jī)制
在Android中使用變量前我們需要對(duì)其進(jìn)行空判斷避免空指針蛔添,這樣往往帶來帶來大量的工作痰催,這些空判斷代碼本身沒有什么實(shí)際意義,并且讓代碼的可讀性和簡(jiǎn)潔性帶來了巨大的挑戰(zhàn)迎瞧。除此之外夸溶,我們經(jīng)常會(huì)忘記判空!P坠琛缝裁!
Kotlin 為了解決這個(gè)問題,它并不允許我們直接使用一個(gè)可選型的變量去調(diào)用方法或者屬性咏尝。對(duì)于聲明可為空的參數(shù)压语,在使用時(shí)要進(jìn)行空判斷處理啸罢,有兩種處理方式,字段后加!!像Java一樣拋出空異常胎食,另一種字段后加?可不做處理返回值為 null或配合?:做空判斷處理
//類型后面加?表示可為空
var age: String? = "23"
//拋出空指針異常
val ages = age!!.toInt()
//不做處理返回 null
val ages1 = age?.toInt()
//age為空返回-1
val ages2 = age?.toInt() ?: -1 //等價(jià)于 if (age != null) age.toInt() else -1
方法(函數(shù))
java中的方法:
public int funName() {
return 1;
}
而在Kotlin函數(shù)定義使用關(guān)鍵字 fun扰才,參數(shù)格式為:參數(shù) : 類型
fun sum(a: Int, b: Int): Int { // Int 參數(shù),返回值 Int
return a + b
}
可變長(zhǎng)參數(shù)函數(shù)
java中使用...
表示可變參數(shù)
函數(shù)的變長(zhǎng)參數(shù)可以用 vararg 關(guān)鍵字進(jìn)行標(biāo)識(shí):
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
todo
之前用java開發(fā)Android的時(shí)候厕怜,對(duì)于一些放在之后實(shí)現(xiàn)的功能衩匣,為了防止忘記,會(huì)加上//TODO
粥航,以方便自己后續(xù)記得在此處補(bǔ)上實(shí)現(xiàn)琅捏。而在Kotlin中,輸入todo递雀,IDE提示的是一個(gè)TODO("xxx")
的函數(shù)柄延,功能類似,只是在調(diào)用此處方法的時(shí)候回拋出一個(gè)異常:
kotlin.NotImplementedError: An operation is not implemented: xxx