這一篇主要講解kotlin進(jìn)階,內(nèi)容主要有以下:
空安全
kotlin單例
函數(shù)擴(kuò)展
智能轉(zhuǎn)換
空安全
Kotlin中添加了對空的保護(hù)
fun testKotlinNPE() {
var s: String = "Hubery"
s = null//會導(dǎo)致編譯不過
var s2: String? = "Hubery"
s2 = null//s2可以為空,能夠編譯通過
print(s.length)//因為不為空,所以直接調(diào)用length不會出現(xiàn)異常
print(s2.length)//編譯不通過,會要求添加?.或者!!.進(jìn)行調(diào)用
print(s2!!.length)//編譯能夠通過,不過s2如果是null,那么將會導(dǎo)致調(diào)用length的時候出現(xiàn)空指針
print(s2?.length)//使用安全操作符,如果s2為null,那么就不會調(diào)用length,也就不會導(dǎo)致異常,不過需要注意的是s2?.length可能整體為null;如果別的地方使用需要注意
//Elvis操作符
val i = s2?.length ?:0//如果s2為空,那么就不會調(diào)用.length方法,而直接返回右邊的0
}
使用Elvis操作符,可以val i = s2?.length ?:0如果s2為空,那么就不會調(diào)用.length方法,而直接返回右邊的0。
kotlin單例
我們都知道單例分為懶漢式與餓漢式,同時可以對單例進(jìn)行線程同步等;
我們先來看看一個java的例子:
/**
* Created by hubery on 2018/7/18.
* 餓漢模式
*/
public class DemoSingleton {
private static DemoSingleton INSTANCE = new DemoSingleton();
public static DemoSingleton getInstance() {
return INSTANCE;
}
}
上面展示的例子就是java的餓漢式模式;那么kotlin的餓漢模式是怎么樣的呢?
/**
* Created by hubery on 2018/7/18.
* kotlin的惡漢模式
*/
object DemoSingleton
沒有看錯就只有一句話,關(guān)鍵字object修飾。當(dāng)然這里因為類里面沒有方法因此省略了{},因為懶漢式是非線程安全的,多線程中使用可能會出現(xiàn)創(chuàng)建多個對象,因此,我們需要用到懶漢式單例,我們把上面的例子稍微改造一下:
public class DemoSingleton {
private static DemoSingleton INSTANCE;
public static DemoSingleton getInstance() {
if(INSTANCE == null){
INSTANCE = new DemoSingleton();
}
return INSTANCE;
}
}
上面的java例子也就相比之前的餓漢式多了一個null的判斷,再看看kotlin的實現(xiàn):
/**
* Created by hubery on 2018/7/18.
* kotlin的懶漢式單例
*/
class DemoSingleton {
companion object {
val INSTANCE by lazy(LazyThreadSafetyMode.NONE) {
DemoSingleton()
}
}
}
kotlin的懶漢式單例中使用到了伴生對象companion object,它相當(dāng)于java中的公共靜態(tài),然后使用到了LazyThreadSafetyMode枚舉里面的NONE;
public enum class LazyThreadSafetyMode {
/**
* Locks are used to ensure that only a single thread can initialize the [Lazy] instance.
*/
SYNCHRONIZED,
/**
* Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value,
* but only the first returned value will be used as the value of [Lazy] instance.
*/
PUBLICATION,
/**
* No locks are used to synchronize an access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined.
*
* This mode should not be used unless the [Lazy] instance is guaranteed never to be initialized from more than one thread.
*/
NONE,
}
通過上面的注釋,可以看出NONE表示是沒有加鎖,因此不是線程安全的,當(dāng)然也可以使用SYNCHRONIZED,PUBLICATION。
上面的例子都會有線程安全問題,現(xiàn)在我們繼續(xù)對代碼進(jìn)行改進(jìn),使用SYNCHRONIZED進(jìn)行加鎖:
/**
* Created by hubery on 2018/7/18.
* 懶漢模式
*/
public class DemoSingleton {
private static DemoSingleton INSTANCE;
public static synchronized DemoSingleton getInstance() {
if(INSTANCE == null){
INSTANCE = new DemoSingleton();
}
return INSTANCE;
}
}
同樣的kotlin也可以使用synchronized進(jìn)行加鎖:
/**
* Created by hubery on 2018/7/18.
* kotlin的懶漢式單例
*/
class DemoSingleton {
companion object {
private var INSTANCE:DemoSingleton? = null
@Synchronized
fun getInstance():DemoSingleton{
if(INSTANCE == null){
INSTANCE = DemoSingleton()
}
return INSTANCE!!
}
}
}
kotlin中使用的是@Synchronized注解方式達(dá)到同步的效果;同樣的,我們知道上面的例子同樣存在不足,我們要實現(xiàn)只有第一次獲取的時候才會加鎖,那么我們使用雙重檢測。
/**
* Created by hubery on 2018/7/18.
* 懶漢模式
*/
public class DemoSingleton {
private static volatile DemoSingleton INSTANCE;
public static DemoSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DemoSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DemoSingleton();
}
}
}
return INSTANCE;
}
}
使用kotlin實現(xiàn)
/**
* Created by hubery on 2018/7/18.
* kotlin的懶漢式單例
*/
class DemoSingleton {
companion object {
val INSTANCE by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
DemoSingleton()
}
}
}
最后說一下內(nèi)部類式
public class DemoSingleton {
private DemoSingleton() {
}
private static class Instance {
private static DemoSingleton singleton = new DemoSingleton();
}
public static DemoSingleton getInstance() {
return Instance.singleton;
}
}
kotlin的寫法
class DemoSingleton {
companion object {
fun getInstance() = Instance.INSTANCE
}
private object Instance {
val INSTANCE = DemoSingleton()
}
}
利用了classloader的機(jī)制來保證初始化instance時只有一個線程,所以也是線程安全的,同時沒有性能損耗。
對比kotlin和java的單例,總體說來kotlin的單例比java單例實現(xiàn)代碼更加的簡潔,且kotlin語言對單例的支持更加的友好。
函數(shù)擴(kuò)展
擴(kuò)展函數(shù)可以在已有類中添加新的方法,不會對原類做修改,擴(kuò)展函數(shù)定義形式。其中擴(kuò)展函數(shù)的參數(shù)可以為空。
fun 擴(kuò)展函數(shù)的對象.擴(kuò)展函數(shù)的名字(擴(kuò)展函數(shù)的參數(shù)){
}
先來看看基本的函數(shù)擴(kuò)展:
open class Personal(var name: String) {
}
/**
* 擴(kuò)展函數(shù)
*/
fun Personal.test(name: String): String {
return "這是一個擴(kuò)展類,名字=$name"
}
/**
* 方法測試
*/
fun test(){
var personal = Personal("Hubery")
personal.test("test()")
}
上面的例子展示了為Personal對象擴(kuò)展一個test方法;
擴(kuò)展函數(shù)是靜態(tài)解析的,并不是接收者類型的虛擬成員,在調(diào)用擴(kuò)展函數(shù)時,具體被調(diào)用的的是哪一個函數(shù),由調(diào)用函數(shù)的的對象表達(dá)式來決定的,而不是動態(tài)的類型決定的:
擴(kuò)展函數(shù)不僅可以擴(kuò)展方法,還可以對屬性進(jìn)行擴(kuò)展,或者伴生類進(jìn)行函數(shù)或者屬性進(jìn)行擴(kuò)展。
智能轉(zhuǎn)換
關(guān)于智能轉(zhuǎn)換,我們用一個例子來說明,先來看看java中的例子:
public interface Ball {
void pay(String name);
}
class BasketBall implements Ball {
@Override
public void pay(String name) {
}
}
class FootBall implements Ball {
@Override
public void pay(String name) {
}
}
void test(Ball ball) {
if (ball instanceof BasketBall) {//籃球
BasketBall basketBall = (BasketBall) ball;
basketBall.pay("籃球");
} else if (ball instanceof FootBall) {
FootBall footBall = (FootBall) ball;
footBall.pay("足球");
}
}
定義一個Ball接口,那么分別用BasketBall,FootBall去實現(xiàn)它,在測試的時候,分別用instanceof去判斷,然后再分別強(qiáng)轉(zhuǎn)成相應(yīng)的類型。
interface Ball {
fun pay(name: String) {
print("pay() 我們正在玩$name")
}
}
class BasketBall : Ball {
override fun pay(name: String) {
super.pay(name)
}
fun basketBallPay() {
}
}
class FootBall : Ball {
override fun pay(name: String) {
super.pay(name)
}
fun footBallPay() {
}
}
fun test(ball: Ball) {
if (ball is BasketBall) {
var basketBall = ball.basketBallPay()
ball.pay("籃球")
} else if (ball is FootBall) {
var footBall = ball.footBallPay()
ball.pay("足球")
}
}
由上面可以看到,kotlin在使用的時候不同于java需要強(qiáng)制轉(zhuǎn)換,kotlin它合并了類型檢查和轉(zhuǎn)換。這就是kotlin的智能轉(zhuǎn)換。