最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android開(kāi)發(fā)必備知識(shí) 為什么說(shuō)Kotlin值得一試

 更新時(shí)間:2017年05月19日 11:21:20   作者:騰訊Bugly  
為什么說(shuō)值得一試,這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)必備知識(shí),Kotlin的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

1、Hello, Kotlin

Bugly 技術(shù)干貨系列內(nèi)容主要涉及移動(dòng)開(kāi)發(fā)方向,是由 Bugly 邀請(qǐng)騰訊內(nèi)部各位技術(shù)大咖,通過(guò)日常工作經(jīng)驗(yàn)的總結(jié)以及感悟撰寫(xiě)而成,內(nèi)容均屬原創(chuàng),轉(zhuǎn)載請(qǐng)標(biāo)明出處。

1.1 Kotlin的身世

  • 寫(xiě)了許久Java,有沒(méi)有發(fā)現(xiàn)其實(shí)你寫(xiě)了太多冗余的代碼?
  • 后來(lái)你體驗(yàn)了一下Python,有沒(méi)有覺(jué)得不寫(xiě)分號(hào)的感覺(jué)真是超級(jí)爽?
  • 你雖然勤勤懇懇,可到頭來(lái)卻被NullPointerException折磨的死去活來(lái),難道就沒(méi)有受夠這種日子么?
  • 直到有一天你發(fā)現(xiàn)自己已經(jīng)寫(xiě)了好幾十萬(wàn)行代碼,發(fā)現(xiàn)居然全是getter和setter!

哈哈,實(shí)際上完全可以不用這么痛苦,用Kotlin替代Java開(kāi)發(fā)你的程序,無(wú)論是Android還是Server,你都能像之前寫(xiě)Java一樣思考,同時(shí)又能享受到新一代編程語(yǔ)言的特性,說(shuō)到這里你是不是開(kāi)始心動(dòng)了呢?下面我就通過(guò)這篇文章來(lái)給大家介紹一下Kotlin究竟是何方神圣。

話說(shuō),Kotlin是JetBrain公司搞出來(lái)的,運(yùn)行在JVM上的一門(mén)靜態(tài)類(lèi)型語(yǔ)言,它是用波羅的海的一個(gè)小島的名字命名的。從外觀上,乍一看還以為是Scala,我曾經(jīng)琢磨著把Scala作為我的下一門(mén)語(yǔ)言,不過(guò)想想用Scala來(lái)干嘛呢,我又不做大數(shù)據(jù),而它又太復(fù)雜了o(╯□╰)o

用Kotlin創(chuàng)建一個(gè)數(shù)據(jù)類(lèi)

data class Mondai(var index: Int = 0,
     var title: String = "",
     val ans: ArrayList<String> = ArrayList(),
     var correct: Int = 0,
     var comment: String = "",
     var color: String = "",
     private var lives: Int = 50)

最初是在intelliJ的源碼中看到Kotlin的,那時(shí)候Kotlin的版本還不太穩(wěn)定,所以源碼總是編譯不過(guò),真是要抓狂啊,還罵『什么破玩意兒!為什么又出來(lái)新語(yǔ)言了?Groovy還沒(méi)怎么學(xué)會(huì),又來(lái)個(gè)Kotlin!』話說(shuō),Kotlin,難道是『靠它靈』的意思??

其實(shí)經(jīng)過(guò)一年多的發(fā)展,Kotlin 1.0已經(jīng)release,feature基本完善,api也趨于穩(wěn)定,這時(shí)候嘗試也不會(huì)有那種被坑的感覺(jué)了。過(guò)年期間也算清閑,于是用Kotlin做了個(gè)app,簡(jiǎn)單來(lái)說(shuō),就是幾個(gè)感覺(jué):

  • 思路與寫(xiě)Java時(shí)一樣,不過(guò)更簡(jiǎn)潔清爽
  • 少了冗余代碼的煩惱,更容易專(zhuān)注于功能的開(kāi)發(fā),整個(gè)過(guò)程輕松愉快
  • 擴(kuò)展功能使得代碼寫(xiě)起來(lái)更有趣
  • 空安全和不可變類(lèi)型使得開(kāi)發(fā)中對(duì)變量的定義和初始化傾注了更多關(guān)注
  • 啊啊,我再也不用寫(xiě)那個(gè)findViewById了,真的爽爆有木有!

1.2 第一個(gè)Kotlin程序

Kotlin開(kāi)發(fā)當(dāng)然使用JetBrain系列的IDE,實(shí)際上intelliJ idea 15發(fā)布時(shí)就已經(jīng)內(nèi)置了Kotlin插件,更早的版本則需要到插件倉(cāng)庫(kù)中下載安裝Kotlin插件——在安裝時(shí)你還會(huì)看到有個(gè)Kotlin Extensions for Android,不要管他,已經(jīng)過(guò)時(shí)了。安裝好以后,我們就可以使用Kotlin進(jìn)行開(kāi)發(fā)了。

接下來(lái)我們用Android Studio創(chuàng)建一個(gè)Android工程,比如叫做HelloKotlin,在app目錄下面的build.gradle文件中添加下面的配置:

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
ext.anko_version = '0.8.2'
ext.kotlin_version = '1.0.0'
……

dependencies{
……
 compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
 compile "org.jetbrains.anko:anko-sdk15:$anko_version"
 compile "org.jetbrains.anko:anko-support-v4:$anko_version"
 compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
……
}

buildscript {
 repositories {
  jcenter()
 }
 dependencies {
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 }
}
……

這里添加了kotlin對(duì)android的擴(kuò)展,同時(shí)也添加了kotlin的gradle插件。

接下來(lái)就可以編寫(xiě)kotlin代碼了——等等,Android Studio會(huì)幫我們生成一個(gè)MainActivity,你可以直接在菜單

Code -> Convert Java file to Kotlin file

將這個(gè)java代碼轉(zhuǎn)換為kotlin代碼。截止到現(xiàn)在,你什么都不用做,程序就已經(jīng)可以跑起來(lái)了。

2、完美為Java開(kāi)發(fā)者打造

2.1 通用的集合框架

我們都知道Jvm上面的語(yǔ)言,像什么Java、Groovy、Jython啥的,都是要編成虛擬機(jī)的字節(jié)碼的,一旦編成字節(jié)碼,在一定程度上大家就都平等了。

英雄不問(wèn)出身啊

有人做過(guò)一個(gè)非常形象的比喻:Java虛擬機(jī)語(yǔ)言就是打群架。Kotlin正是充分利用了這一點(diǎn),它自己的標(biāo)準(zhǔn)庫(kù)只是基于Java的語(yǔ)言框架做了許多擴(kuò)展,你在Kotlin當(dāng)中使用的集合框架仍然跟你在Java當(dāng)中一樣。

舉個(gè)例子,如果你想要在Kotlin中使用ArrayList,很簡(jiǎn)單,Java的ArrayList你可以隨意使用,這個(gè)感覺(jué)跟使用Java沒(méi)有任何區(qū)別,請(qǐng)看:

//實(shí)際上就是創(chuàng)建一個(gè)ArrayList
 val list = arrayListOf(1,2,3,4)
 list.add(5)
 list.remove(3)
 for(item in list){
  println(item)
 }

當(dāng)然,Kotlin標(biāo)準(zhǔn)庫(kù)也對(duì)這些做了擴(kuò)展,我們?cè)谙碛肑ava世界的一切資源的同時(shí),還能比原生Java代碼更滋潤(rùn),真是爽爆有木有:

 val list = arrayListOf(1, 2, 3, 4, 5)
 
 //doubleList = [2,4,6,8,10]
 val doubleList = list.map { 
  it * 2
 }
 
 //oddList = [1,3,5]
 val oddList = list.filter{
  it % 2 == 1
 }
 
 //將list挨個(gè)打印出來(lái)
 list.forEach { 
  println(it)
 }

2.2 與Java交互

Kotlin的標(biāo)準(zhǔn)庫(kù)更多的是對(duì)Java庫(kù)的擴(kuò)展,基于這個(gè)設(shè)計(jì)思路,你絲毫不需要擔(dān)心Kotlin對(duì)Java代碼的引用,你甚至可以在Kotlin當(dāng)中使用Java反射,反正只要是Java有的,Kotlin都有,于是有人做出這樣的評(píng)價(jià):

Kotlin就是Java的一個(gè)擴(kuò)展
這樣說(shuō)Kotlin顯然是不公平的,但就像微信剛面世那會(huì)兒要為QQ接收離線消息一樣,總得抱幾天大腿嘛。

有關(guān)從Kotlin中調(diào)用Java的官方文檔在此[Calling Java code from Kotlin
](https://kotlinlang.org/docs/r...,其中最常見(jiàn)的就是Getter/Setter方法對(duì)應(yīng)到Kotlin屬性的調(diào)用,舉個(gè)例子:

準(zhǔn)備一個(gè)Java類(lèi)

public class JavaClass {
 private int anInt = 0;

 public int getAnInt() {
  return anInt;
 }

 public void setAnInt(int anInt) {
  this.anInt = anInt;
 }
}

下面是Kotlin代碼

 val javaClass = JavaClass()
 javaClass.anInt = 5
 print(javaClass.anInt)

所以我們?cè)贏ndroid開(kāi)發(fā)時(shí),就可以這樣:

view.background = ...
textView.text = ...

反過(guò)來(lái)在Java中調(diào)用Kotlin也毫無(wú)壓力,官方文檔Calling Kotlin from Java對(duì)于常見(jiàn)的情況作了比較詳細(xì)的闡述,這里就不再贅述。

3、簡(jiǎn)潔,可靠,有趣

3.1 數(shù)據(jù)類(lèi)

最初學(xué)Java的時(shí)候,學(xué)到一個(gè)概念叫JavaBean,當(dāng)時(shí)就要被這個(gè)概念給折磨死了。明明很簡(jiǎn)單的一個(gè)東西,結(jié)果搞得很復(fù)雜的樣子,而且由于當(dāng)時(shí)對(duì)于這些數(shù)據(jù)類(lèi)的設(shè)計(jì)概念不是很清晰,因而也并不懂得去覆寫(xiě)諸如equals和hashcode這樣重要的方法,一旦用到HashMap這樣的集合框架,總是出了問(wèn)題都不知道找誰(shuí)。

Kotlin提供了一種非常簡(jiǎn)單的方式來(lái)創(chuàng)建這樣的數(shù)據(jù)類(lèi),例如:

data class Coordinate(val x: Double, val y: Double)

僅僅一行代碼,Kotlin就會(huì)創(chuàng)建出一個(gè)完整的數(shù)據(jù)類(lèi),并自動(dòng)生成相應(yīng)的equals、hashcode、toString方法。是不是早就受夠了getter和setter?反正我是受夠了。

3.2 空安全與屬性

第一次見(jiàn)到空類(lèi)型安全的設(shè)計(jì)是在Swift當(dāng)中,那時(shí)候還覺(jué)得這個(gè)東西有點(diǎn)兒意思哈,一旦要求變量不能為空以后,因它而導(dǎo)致的空指針異常的可能性就直接沒(méi)有了。想想每次QA提的bug吧,說(shuō)少了都得有三分之一是空指針吧。

Kotlin的空安全設(shè)計(jì),主要是在類(lèi)型后面加?表示可空,否則就不能為null。

val anInt: Int = null // 錯(cuò)誤
val anotherInt: Int? = null // 正確

使用時(shí),則:

val nullable: Int? = 0
val nonNullable: Int = 2
nullable.toFloat() // 編譯錯(cuò)誤
nullable?.toFloat() // 如果null,什么都不做,否則調(diào)用toFloat
nullable!!.toFloat() // 強(qiáng)制轉(zhuǎn)換為非空對(duì)象,并調(diào)用toFloat;如果nullable為null,拋空指針異常
nonNullable.toFloat() // 正確

而對(duì)于Java代碼,比如我們?cè)诟矊?xiě)Activity的onCreate方法時(shí),有個(gè)參數(shù)savedInstanceState:

override fun onCreate(savedInstanceState: Bundle!)

這表示編譯器不再?gòu)?qiáng)制savedInstanceState是否可null,開(kāi)發(fā)者在覆寫(xiě)時(shí)可以自己決定是否可null。當(dāng)然,對(duì)于本例,onCreate的參數(shù)是可能為null的,因此覆寫(xiě)以后的方法應(yīng)為:

override fun onCreate(savedInstanceState: Bundle?)

通常來(lái)講,教科書(shū)式,到這里就該結(jié)束了。然而直到我真正用Kotlin開(kāi)始寫(xiě)代碼時(shí),發(fā)現(xiàn),有些需求實(shí)現(xiàn)起來(lái)真的有些奇怪。

還是舉個(gè)例子,我需要在Activity當(dāng)中創(chuàng)建一個(gè)View的引用,通常我們?cè)贘ava代碼中這么寫(xiě):

public class DemoActivity extends Activity{
 
 private TextView aTextView;
 
 public void onCreate(Bundle savedInstanceState){
  super.OnCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  aTextView = (TextView) findViewById(R.id.a_textview);
  aTextView.setText("Hello");
  aTextView.setTextSize(20);
  ...
 }
}

在Kotlin當(dāng)中呢?

class DemoActivity : Activity(){
 private var aTextView: TextView? = null
 
 override fun onCreate(savedInstanceState: Bundle?){
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main)
  //當(dāng)然有更好用的方式,暫且先這么寫(xiě)
  aTextView = findViewById(R.id.a_textview) as TextView
  aTextView!!.text = "Hello"
  aTextView!!.textSize = 20
  ...
 }
}

每次用aTextView都要加倆!,不然編譯器不能確定它究竟是不是null,于是不讓你使用。。這。。。到底是為了方便還是為了麻煩??

所以后來(lái)我又決定這么寫(xiě):

class DemoActivity : Activity(){
 private var aTextView: TextView // 編譯錯(cuò)誤,必須初始化?。?!
 
 ...
}

這可如何是好??

其實(shí)Kotlin肯定是有辦法解決這個(gè)問(wèn)題噠!比如上面的場(chǎng)景,我們這么寫(xiě)就可以咯:

class DemoActivity : Activity(){
 private val aTextView: TextView by lazy{
  findViewById(R.id.a_textview) as TextView
 }
 
 override fun onCreate(savedInstanceState: Bundle?){
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main)
  aTextView.text = "Hello"
  aTextView.textSize = 20
  ...
 }
}

lazy是Kotlin的屬性的一個(gè)實(shí)例,它提供了延遲加載的機(jī)制。換句話說(shuō),這里的lazy提供了初始化aTextView的方法,不過(guò)真正初始化這個(gè)動(dòng)作發(fā)生的時(shí)機(jī)卻是在aTextView第一次被使用時(shí)了。lazy默認(rèn)是線程安全的,你當(dāng)然也可以關(guān)掉這個(gè)配置,只需要加個(gè)參數(shù)即可:

private val aTextView: TextView by lazy(LazyThreadSafetyMode.NONE){
  findViewById(R.id.a_textview) as TextView
}

好,這時(shí)候肯定有人要扔西紅柿過(guò)來(lái)了(再扔點(diǎn)兒雞蛋唄),你這lazy只能初始化val啊,萬(wàn)一我要定義一個(gè)var成語(yǔ),又需要延遲初始化,關(guān)鍵還不為null,怎么辦??

class Demo {
 lateinit var anJsonObject: JsonObject
 
 fun initDemo(){
  anJsonObject = JsonObject("{...}")
 }
 
}

lateinit的使用還是有很多限制的,比如只能在不可null的對(duì)象上使用,比須為var,不能為primitives(Int、Float之類(lèi))等等,不過(guò)這樣逼迫你一定要初始化這個(gè)變量的做法,確實(shí)能減少我們?cè)陂_(kāi)發(fā)中的遺漏,從而提高開(kāi)發(fā)效率。

至于lazy技術(shù),實(shí)際上是Delegate Properties的一個(gè)應(yīng)用,也就是屬性了。在Kotlin當(dāng)中,聲明成員屬性,除了直接賦值,還可以用Delegate的方式來(lái)聲明,這個(gè)Delegate需要根據(jù)成員的類(lèi)型(val或者var)來(lái)提供相應(yīng)的getValue和setValue方法,比如一個(gè)可讀寫(xiě)的Delegate,需要提供下面的方法:

public interface ReadWriteProperty<in R, T> {
 /**
  * Returns the value of the property for the given object.
  * @param thisRef the object for which the value is requested.
  * @param property the metadata for the property.
  * @return the property value.
  */
 public operator fun getValue(thisRef: R, property: KProperty<*>): T

 /**
  * Sets the value of the property for the given object.
  * @param thisRef the object for which the value is requested.
  * @param property the metadata for the property.
  * @param value the value to set.
  */
 public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

好嘴皮不如栗子,下面我們就看一個(gè)自定義Delegate,用來(lái)訪問(wèn)SharedPreference:

class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> {

 val prefs by lazy { context.getSharedPreferences("default", Context.MODE_PRIVATE) }

 override fun getValue(thisRef: Any?, property: KProperty<*>): T {
  return findPreference(name, default)
 }

 override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
  putPreference(name, value)
 }

 private fun <U> findPreference(name: String, default: U): U = with(prefs) {
  val res: Any = when (default) {
   is Long -> getLong(name, default)
   is String -> getString(name, default)
   is Int -> getInt(name, default)
   is Boolean -> getBoolean(name, default)
   is Float -> getFloat(name, default)
   else -> throw IllegalArgumentException("This type can be saved into Preferences")
  }

  res as U
 }

 private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
  when (value) {
   is Long -> putLong(name, value)
   is String -> putString(name, value)
   is Int -> putInt(name, value)
   is Boolean -> putBoolean(name, value)
   is Float -> putFloat(name, value)
   else -> throw IllegalArgumentException("This type can be saved into Preferences")
  }.apply()
 }
}

需要說(shuō)明的是,這段代碼是我從《Kotlin for Android Developer》的示例中摘出來(lái)的。有了這個(gè)Delegate類(lèi),我們就可以完全不需要關(guān)心SharedPreference了,下面給出使用的示例代碼:

class WhateverActivity : Activity(){
 var aInt: Int by Preference(this, "aInt", 0)
 
 fun whatever(){
  println(aInt)//會(huì)從SharedPreference取這個(gè)數(shù)據(jù)
  aInt = 9 //會(huì)將這個(gè)數(shù)據(jù)寫(xiě)入SharedPreference
 }
}

于是我們?cè)僖膊恍枰貜?fù)寫(xiě)那些getSharedPreference,也不用edit、commit,再見(jiàn)那些edit之后忘了commit的日子。有沒(méi)有覺(jué)得非常贊!

3.3 擴(kuò)展類(lèi)

擴(kuò)展類(lèi),就是在現(xiàn)有類(lèi)的基礎(chǔ)上,添加一些屬性或者方法,當(dāng)然擴(kuò)展的這些成員需要導(dǎo)入當(dāng)前擴(kuò)展成員所在的包才可以訪問(wèn)到。下面給出一個(gè)例子:

data class Coordinate(val x: Double, val y: Double)

val Coordinate.theta: Double
 get() {
  return Math.atan(y/x)
 }

fun Coordinate.R():Double{
 return Math.hypot(x, y)
}

我們已經(jīng)介紹過(guò)data class,Coordinate有兩個(gè)成員分別是x和y,我們知道通常表示一個(gè)二維平面,有這倆夠了;然而我們?cè)趫D形學(xué)當(dāng)中經(jīng)常會(huì)需要求得其極坐標(biāo),所以我們擴(kuò)展了Coordinate,增加了一個(gè)屬性theta表示角度(反正切的值域?yàn)?π/2 ~ π/2,所以這個(gè)式子不適用于二三象限,不過(guò)這不是重點(diǎn)了),增加了一個(gè)R方法來(lái)獲得點(diǎn)的半徑,于是我們?cè)趍ain方法中就可以這么用:

fun main(args: Array<String>) {
 val coord = Coordinate(3.0,4.0)
 println(coord.theta)
 println(coord.R())
}

那么這個(gè)擴(kuò)展有什么限制呢?

在擴(kuò)展成員當(dāng)中,只能訪問(wèn)被擴(kuò)展類(lèi)在當(dāng)前作用域內(nèi)可見(jiàn)的成員,本例中的x和y都是public的(Kotlin默認(rèn)public,這個(gè)我們后面會(huì)提到),所以可以在擴(kuò)展方法和屬性中直接訪問(wèn)。
擴(kuò)展成員與被擴(kuò)展類(lèi)的內(nèi)部成員名稱(chēng)相同時(shí),擴(kuò)展成員將無(wú)法被訪問(wèn)到
好的,基本知識(shí)就是這些了,下面我們?cè)俳o出一個(gè)實(shí)際的例子。

通常我們?cè)贘ava中會(huì)自定義一些LogUtils類(lèi)來(lái)打日志,或者直接用android.util.log來(lái)輸出日志,不知道大家是什么感受,我反正每次因?yàn)橐斎隠og.d還要輸入個(gè)tag簡(jiǎn)直煩的要死,而且有時(shí)候恰好這個(gè)類(lèi)還沒(méi)有tag這個(gè)成員,實(shí)踐中我們通常會(huì)把當(dāng)前類(lèi)名作為T(mén)AG,但每個(gè)類(lèi)都要做這么個(gè)工作,是在是沒(méi)有什么趣味可言(之前我是用LiveTemplates幫我的,即便如此也沒(méi)有那種流暢的感覺(jué))。

有了Kotlin的這個(gè)擴(kuò)展功能,日子就會(huì)好過(guò)得多了,下面我創(chuàng)建的一個(gè)打日志的方法:

package com.benny.utils

import android.util.Log

inline fun <reified T> T.debug(log: Any){
 Log.d(T::class.simpleName, log.toString())
}

有了這個(gè)方法,你可以在任何類(lèi)的方法體中直接寫(xiě):

debug(whatever)

然后就會(huì)輸出以這個(gè)類(lèi)名為T(mén)AG的日志。

嗯,這里需要簡(jiǎn)單介紹Kotlin在泛型中的一個(gè)比較重要的增強(qiáng),這個(gè)在Java中無(wú)論如何也是做不到的:inline、reified。我們?cè)賮?lái)回頭看一下debug這個(gè)方法,我們發(fā)現(xiàn)它可以通過(guò)泛型參數(shù)T來(lái)獲取到T的具體類(lèi)型,并且拿到它的類(lèi)名——當(dāng)然,如果你愿意,你甚至可以調(diào)用它的構(gòu)造方法來(lái)構(gòu)造一個(gè)對(duì)象出來(lái)——為什么Kotlin可以做到呢?因?yàn)檫@段代碼是inline的,最終編譯時(shí)是要編譯到調(diào)用它的代碼塊中,這時(shí)候T的類(lèi)型實(shí)際上是確定的,因而Kotlin通過(guò)reified這個(gè)關(guān)鍵字告訴編譯器,T這個(gè)參數(shù)可不只是個(gè)擺設(shè),我要把它當(dāng)實(shí)際類(lèi)型來(lái)用呢。

為了讓大家印象深刻,我下面給出類(lèi)似功能的Java的代碼實(shí)現(xiàn):

 public static void debug(Class<?> clazz, Object log){
  Log.d(clazz.getSimpleName(), log.toString());
 }

而你如果說(shuō)希望在Java中也希望像下面這樣拿到這個(gè)泛型參數(shù)的類(lèi)型,是不可以的:

public static <T> void debug(Object log){
 Log.d(T.getSimpleName(), log.toString());//錯(cuò)誤,T是泛型參數(shù),無(wú)法直接使用
}

就算我們?cè)谡{(diào)用處會(huì)寫(xiě)道 debug < Date >("blabla"),但這個(gè)Date在編譯之后還是會(huì)被擦除。

3.4 函數(shù)式支持(Lambdas)

Java 8已經(jīng)開(kāi)始可以支持Lambda表達(dá)式了,這種東西對(duì)于Java這樣一個(gè)『根紅苗正』的面向?qū)ο缶幊陶Z(yǔ)言來(lái)說(shuō)還真是顯得不自然,不過(guò)對(duì)于Kotlin來(lái)說(shuō),就沒(méi)那么多顧忌了。

通常我們需要執(zhí)行一段異步的代碼,我們會(huì)構(gòu)造一個(gè)Runnable對(duì)象,然后交給executor,比如這段java代碼:

executor.submit(new Runnable(){
 @Override
 public void run(){
  //todo
 }
});

用Kotlin怎么寫(xiě)呢?

executor.submit({
 //todo
})

一下子省了很多代碼。

那么實(shí)際當(dāng)中我們可能更常見(jiàn)到下面的例子,這是一段很常見(jiàn)的Java代碼,在Android的UI初始化會(huì)見(jiàn)到:

textView.setOnClickListener(new OnClickListener(){
 @Override
 public void onClick(View view){
  //todo
 }
});

handle.post(new Runnable(){
 @Override
 public void run(){
  //todo
 }
});

那么我們用Kotlin怎么寫(xiě)呢?

textView.setOnClickListener{ /*todo*/ }
handler.post{ /*todo*/ }

在Anko這個(gè)Android庫(kù)的幫助下,我們甚至可以繼續(xù)簡(jiǎn)化OnClickListener的設(shè)置方式:

textView.onClick{ /*todo*/ }

當(dāng)然,好玩的不止這些,如果結(jié)合上一節(jié)我們提到的擴(kuò)展方法,我們就很容易看到Kotlin的標(biāo)準(zhǔn)庫(kù)提供的類(lèi)似with和apply這樣的方法是怎么工作的了:

public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

我們通常會(huì)在某個(gè)方法體內(nèi)創(chuàng)建一個(gè)對(duì)象并返回它,可我們除了調(diào)用它的構(gòu)造方法之外還需要做一些其他的操作,于是就要?jiǎng)?chuàng)建一個(gè)局部變量。。。有了apply這個(gè)擴(kuò)展方法,我們就可以這么寫(xiě):

fun getStringBuilder: StringBuilder{
 return StringBuilder().apply{
  append("whatever")
 }
}

這樣返回的StringBuilder對(duì)象實(shí)際上是包含"whatever"這個(gè)字符串的。

至于說(shuō)Kotlin對(duì)于RxJava的友好性,使得我突然有點(diǎn)兒相信緣分這種東西了:

  Observable.create<ArrayList<Dummy>> {
   it.onStart()
   try {
    it.onNext(dummyObjs)
   } catch(e: Exception) {
    it.onError(e)
   } finally {
    it.onCompleted()
   }
  }.subscribe(object : Subscriber<ArrayList<Dummy>>() {
   override fun onCompleted() {

   }

   override fun onNext(t: ArrayList<Dummy>?) {

   }

   override fun onError(e: Throwable?) {

   }

  })

3.5 Pattern Matching

記得之前在瀏覽Scala的特性時(shí),看到:

object HelloScala{
 // do something
}

覺(jué)得很新鮮,這時(shí)候有個(gè)朋友不屑的說(shuō)了句,Scala的模式匹配才真正犀利——Kotlin當(dāng)中也有這樣的特性,我們下面就來(lái)看個(gè)例子:

val x = 7
when (x) {
 in 1..10 -> print("x is in the range")
 in validNumbers -> print("x is valid")
 !in 10..20 -> print("x is outside the range")
 else -> print("none of the above")
}

咋一看感覺(jué)when表達(dá)式就是一個(gè)增強(qiáng)版的switch——Java 7以前的switch實(shí)際上支持的類(lèi)型非常有限,Java 7當(dāng)中增加的對(duì)String的支持也是基于int類(lèi)型的——我們可以看到when不再像switch那樣只匹配一個(gè)數(shù)值,它的子式可以是各種返回Boolean的表達(dá)式。

when表達(dá)式還有一種寫(xiě)法:

when {
 x.isOdd() -> print("x is odd")
 x.isEven() -> print("x is even")
 else -> print("x is funny")
}

只要是返回Boolean的表達(dá)式就可以作為when的子式,這樣when表達(dá)式的靈活性可見(jiàn)一斑。當(dāng)然,與Scala相比,Kotlin還是要保守一些的,下面給出一個(gè)Scala類(lèi)似的例子,大家感受一下,這實(shí)際上也可以體現(xiàn)出Kotlin在增加Java的同時(shí)也盡量保持簡(jiǎn)單的設(shè)計(jì)哲學(xué)(大家都知道,畢竟Scala需要智商o(╯□╰)o)。

object Hello {
 def main(args: Array[String]) {
 easyMatch((1, 3))
 easyMatch(Array(1,3,4))
 easyMatch(Bean(3.0, 4.0))
 }

 def easyMatch(value : Any) = value match {
  case int :Int => {
  println("This is an Int.")
  }
  case (a, b) =>{
  println(s"a tuple with : $a , $b")
  }
  case Bean(x, y) => {
  println(s"$x, $y")
 }
  case whatever => println(whatever)
 }
}

case class Bean(val x: Double, val y: Double)

運(yùn)行結(jié)果如下:

a tuple with : 1 , 3
[I@2d554825
3.0, 4.0

3.6 如果你是一個(gè)SDK開(kāi)發(fā)者

我曾經(jīng)做過(guò)一段時(shí)間的SDK開(kāi)發(fā),SDK的內(nèi)部有很多類(lèi)其實(shí)是需要互相有訪問(wèn)權(quán)限的,但一旦類(lèi)及其成員是public的,那么調(diào)用方也就可以看到它們了;而protected或者default這樣的可見(jiàn)性對(duì)于子包卻是不可見(jiàn)的。

用了這么久Java,這簡(jiǎn)直是我唯一強(qiáng)烈感到不滿(mǎn)的地方了,甚至于我突然明白了C++的friend是多么的有用。

Kotlin雖然沒(méi)有提供對(duì)于子包可見(jiàn)的修飾符,不過(guò)它提供了internal:即模塊內(nèi)可見(jiàn)。換句話說(shuō),internal在模塊內(nèi)相當(dāng)于public,而對(duì)于模塊外就是private了——于是乎我們?nèi)绻_(kāi)發(fā)SDK,那么可以減少api層的編寫(xiě),那些用戶(hù)不可見(jiàn)的部分直接用internal豈不更好。當(dāng)然有人會(huì)說(shuō)我們應(yīng)當(dāng)有proguard做混淆,我想說(shuō)的是,proguard自然是要用到的,不過(guò)那是SDK這個(gè)產(chǎn)品加工的下一個(gè)環(huán)節(jié)了,我們?yōu)槭裁床荒茉诖a級(jí)別把這個(gè)事情做好呢?

關(guān)于Kotlin的默認(rèn)可見(jiàn)性究竟是哪個(gè)還有人做出過(guò)討論,有興趣的可以參考這里:Kotlin’s default visibility should be internal。

3.7 DSL

其實(shí)我們對(duì)DSL肯定不會(huì)陌生,gradle的腳本就是基于groovy的DSL,而Kotlin的函數(shù)特性顯然也是可以支持DSL的。比如,我們最終要生成下面的xml數(shù)據(jù):

<project version="4">
 <component name="Encoding">
 <file url="PROJECT" charset="UTF-8" />
 </component>
</project>

我們可以構(gòu)建下面的類(lèi):

class Project {
 var version: String? = null
  get() =
  if (field == null) ""
  else {
   " version=\"${field}\""
  }

 lateinit private var component: Component

 fun component(op: Component.() -> Unit) {
  component = Component().apply {
   op()
  }
 }

 override fun toString(): String {
  return "<project${version}>${component}<project>"
 }


}

fun project(op: Project.() -> Unit): Project {
 return Project().apply {
  op()
 }
}

class Component {
 var name: String? = null
  get() =
  if (field == null) ""
  else {
   " name=\"${field}\""
  }


 lateinit private var file: File

 fun file(op: File.() -> Unit) {
  file = File().apply {
   op()
  }
 }

 override fun toString(): String {
  return "<component${name}>${file}<component>"
 }
}

class File {
 var url: String? = null
  get() =
  if (field == null) ""
  else {
   " url=\"${field}\""
  }

 var charset: String? = null
  get() =
  if (field == null) ""
  else {
   " charset=\"${field}\""
  }

 override fun toString(): String {
  return "<file${url}${charset}/>"
 }
}

fun main(args: Array<String>) {
 val xml = project {
  version = "4"
  component {
   name = "Encoding"
   file {
    url = "PROJECT"
    charset = "UTF-8"
   }
  }
 }

 println(xml)
}

我們看到在main方法當(dāng)中,我們用kotlin定義的dsl寫(xiě)出了一個(gè)Project對(duì)象,它有這與xml描述的一致的結(jié)構(gòu)和含義,如果你愿意,可以構(gòu)造相應(yīng)的方法來(lái)輸出這樣的xml,運(yùn)行之后的結(jié)果:

<project version="4"><component name="Encoding"><file url="PROJECT" charset="UTF-8"/><component><project>
當(dāng)然,這個(gè)例子做的足夠的簡(jiǎn)陋,如果你有興趣也可以抽象出"Element",并為之添加"Attributes",實(shí)際上這也不是很難。

3.7 Kotlin與Android的另一些有趣的東西

寫(xiě)了很多代碼,卻發(fā)現(xiàn)它們干不了多少事情,終究還是會(huì)苦惱的。比如我一直比較痛苦的一件事兒就是:

Button button = (Button) findViewById(R.id.btn);
如果我需要很多個(gè)按鈕和圖片,那么我們要寫(xiě)一大堆這樣的findViewById。。。這活干不了啦。。

不過(guò)用Kotlin的Android擴(kuò)展插件,我們就可以這樣:

先上布局文件:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/open_bj"
    android:orientation="vertical">

  <TextView
   android:id="@+id/textView"  
   android:text="Hello"
   android:textSize="50sp"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

  <Button
   android:id="@+id/start"
   android:clickable="false"
   android:layout_gravity="center_horizontal"
   android:background="@drawable/start_selector"
   android:textSize="50sp"
   android:layout_marginTop="20dp"
   android:layout_marginBottom="200dp"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

</RelativeLayout>

在Activity中:

package com.benny

……
import kotlinx.android.synthetic.main.load_activity.*
import org.jetbrains.anko.onClick
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
……

class LoadActivity : Activity() {
 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main)

  start.onClick {
   toast("開(kāi)始")
   startActivity<AnotherActivity>()
  }
  textView.text = "你好"
    
 }
}

注意到:

import kotlinx.android.synthetic.main.load_activity.*
導(dǎo)入這一句之后,我們就可以直接在代碼中使用start、textView,他們分別對(duì)應(yīng)于main.xml中的id為start的按鈕和id為textView的TextView。

于是你就發(fā)現(xiàn)你再也不用findViewById了,多么愉快的一件事!??!當(dāng)然,你還會(huì)發(fā)現(xiàn)Toast的調(diào)用也變得簡(jiǎn)單了,那其實(shí)就是一個(gè)擴(kuò)展方法toast();而startActivity呢,其實(shí)就是一個(gè)inline加reified的應(yīng)用——這我們前面都提到過(guò)了。

還有一個(gè)惡心的東西就是UI線程和非UI線程的切換問(wèn)題。也許你會(huì)用handler不斷的post,不過(guò)說(shuō)真的,用Handler的時(shí)候難道你不顫抖么,那可是一個(gè)很容易內(nèi)存泄露的魔鬼呀~哈哈,好吧其實(shí)我不是說(shuō)這個(gè),主要是用handler寫(xiě)出來(lái)的代碼 實(shí)在 太 丑 了 ??!

原來(lái)在java當(dāng)中,我們這么寫(xiě):

handler.post(new Runnable(){
 @Override
 public void run(){
  //todo 
 }
});

MainActivity.this.runOnUiThread(
 public void run(){
  //todo
 }
});

而在Kotlin當(dāng)中呢,我們只需要這么寫(xiě):

async() {
 //do something asynchronously
 uiThread {
  //do something on UI thread
 }
}

自己感受一下吧。

下面我們?cè)賮?lái)提一個(gè)有意思的東西,我們從做Android開(kāi)發(fā)一開(kāi)始就要編寫(xiě)xml,印象中這個(gè)對(duì)于我來(lái)說(shuō)真的是一件痛苦的事情,因?yàn)樗墓ぷ鳈C(jī)制并不如代碼那樣直接(以至于我現(xiàn)在很多時(shí)候居然喜歡用Java代碼直接寫(xiě)布局)——當(dāng)然,最主要的問(wèn)題并不是這個(gè),而是解析xml需要耗費(fèi)CPU。Kotlin有辦法可以解決這個(gè)問(wèn)題,那就是DSL了。下面給出一個(gè)例子:

linearLayout {
 button("Login") {
  textSize = 26f
 }.lparams(width = wrapContent) {
  horizontalMargin = dip(5)
  topMargin = dip(10)
 }
}

一個(gè)LinearLayout包含了一個(gè)Button,這段代碼你可以直接寫(xiě)到你的代碼中靈活復(fù)用,就像這樣:

 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  setContentView(linearLayout {
   button("This is a button") {
    onClick {
     toast("clicked!")
    }
   }.lparams {
    width = matchParent
    verticalMargin = dip(5)
   }
  })
 }

這樣做的好處真是不少:

比起xml的繁瑣來(lái),這真是要清爽很多
布局本身也是代碼,可以靈活復(fù)用
再也不用findViewById了,難道你不覺(jué)得在這個(gè)上面浪費(fèi)的生命已經(jīng)足夠多嗎
事件很方便的嵌到布局當(dāng)中
DSL方式的布局沒(méi)有運(yùn)行時(shí)的解析的負(fù)擔(dān),你的邏輯代碼怎么運(yùn)行它就怎么運(yùn)行
Anko還增加了更多好玩的特性,有興趣的可以參考:Anko@Github

3.8 方法數(shù)之痛

我曾經(jīng)嘗試用Scala寫(xiě)了個(gè)Android的HelloWorld,一切都配置好以后,僅僅引入了Scala常見(jiàn)的幾個(gè)庫(kù),加上support-v4以及appcompat這樣常見(jiàn)的庫(kù),結(jié)果還是報(bào)錯(cuò)了。是的,65K。。。而且用Scala開(kāi)發(fā)Android的話,基于gradle的構(gòu)建會(huì)讓整個(gè)app的build過(guò)程異常漫長(zhǎng),有時(shí)候你會(huì)覺(jué)得自己悟出了廣義相對(duì)論的奧義,哦不,你一定是暈了,時(shí)間并沒(méi)有變慢。

相比之下,Kotlin的標(biāo)準(zhǔn)庫(kù)只有7000個(gè)方法,比support-v4還要小,這正反映了Kotlin的設(shè)計(jì)理念:100% interoperable with Java。其實(shí)我們之前就提到,Java有的Kotlin就直接拿來(lái)用,而Scala的標(biāo)準(zhǔn)庫(kù)要有5W多個(gè)方法,想想就還是想想算了。

4、小結(jié)

目前Kotlin 1.0已經(jīng)release,盡管像0xffffffff識(shí)別成Long類(lèi)型這樣的bug仍然沒(méi)有解詳情

val int: Int = 0xffffffff // error
val anotherInt: Int = 0xffffffff.toInt() // correct

不過(guò),Kotlin的教學(xué)資源和社區(qū)建設(shè)也已經(jīng)相對(duì)成熟,按照官方的說(shuō)法,Kotlin可以作為生產(chǎn)工具投入開(kāi)發(fā),詳情可以參考:Kotlin 1.0 Released: Pragmatic Language for JVM and Android。

敢于吃螃蟹,多少有些浪漫色彩,我們這些程序員多少可以有些浪漫特質(zhì),不過(guò)在生成環(huán)境中,穩(wěn)定高于一切仍然是不二法則。追求新技術(shù),一方面會(huì)給團(tuán)隊(duì)帶來(lái)開(kāi)發(fā)和維護(hù)上的學(xué)習(xí)成本,另一方面也要承擔(dān)未來(lái)某些情況下因?yàn)閷?duì)新技術(shù)不熟悉而產(chǎn)生未知問(wèn)題的風(fēng)險(xiǎn)——老板們最怕風(fēng)險(xiǎn)了~~

基于這一點(diǎn),毫無(wú)疑問(wèn),Kotlin可以作為小工具、測(cè)試用例等的開(kāi)發(fā)工具,這是考慮到這些代碼通常體量較小,維護(hù)人數(shù)較少較集中,對(duì)項(xiàng)目整體的影響也較小;而對(duì)于核心代碼,則視情況而定吧。

就我個(gè)人而言,長(zhǎng)期下去,Kotlin很大可能會(huì)成為我的主要語(yǔ)言,短期內(nèi)則仍然采用溫和的改革方式慢慢將Kotlin滲透進(jìn)來(lái)。

一句話,Kotlin是用來(lái)提升效率的,如果在你的場(chǎng)景中它做不到,甚至成了拖累,請(qǐng)放開(kāi)它。

相關(guān)文章

  • Android實(shí)現(xiàn)手機(jī)聯(lián)系人分欄效果

    Android實(shí)現(xiàn)手機(jī)聯(lián)系人分欄效果

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)手機(jī)聯(lián)系人分欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Android異常 java.lang.IllegalStateException解決方法

    Android異常 java.lang.IllegalStateException解決方法

    這篇文章主要介紹了Android異常 java.lang.IllegalStateException解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • 詳解Android 語(yǔ)音播報(bào)實(shí)現(xiàn)方案(無(wú)SDK)

    詳解Android 語(yǔ)音播報(bào)實(shí)現(xiàn)方案(無(wú)SDK)

    本篇文章主要介紹了詳解Android 語(yǔ)音播報(bào)實(shí)現(xiàn)方案(無(wú)SDK),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • Android SwipereFreshLayout下拉刷新

    Android SwipereFreshLayout下拉刷新

    這篇文章主要介紹了Android SwipereFreshLayout下拉刷新的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Android中Fragment管理及重疊問(wèn)題的解決方法

    Android中Fragment管理及重疊問(wèn)題的解決方法

    最近做項(xiàng)目碰到了Fragment重疊的問(wèn)題,后來(lái)通過(guò)種種方法得以解決了,所以想著總結(jié)下這個(gè)問(wèn)題的解決方法,以及Android中Fragment的管理,方便自己也給有需要的朋友們提供以幫助,感興趣的朋友們下面通過(guò)這篇文章一起來(lái)學(xué)習(xí)學(xué)習(xí)吧
    2016-11-11
  • Android實(shí)現(xiàn)socket通信統(tǒng)一接口的方法

    Android實(shí)現(xiàn)socket通信統(tǒng)一接口的方法

    這篇文章主要介紹了Android實(shí)現(xiàn)socket通信統(tǒng)一接口?,實(shí)現(xiàn)了統(tǒng)一接口之后確實(shí)可以使后續(xù)修改實(shí)現(xiàn)更加方便,程序結(jié)構(gòu)也更加工程化,需要的朋友可以參考下
    2021-12-12
  • Android集成微信支付功能

    Android集成微信支付功能

    這篇文章主要為大家詳細(xì)介紹了Android集成微信支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架搭建

    Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架搭建

    這篇文章主要給大家介紹的是Android 架構(gòu)之?dāng)?shù)據(jù)庫(kù)框架搭建,在本篇中,將會(huì)讓你一點(diǎn)一滴從無(wú)到有創(chuàng)建一個(gè)不再為數(shù)據(jù)庫(kù)而煩惱的框架。需要的朋友可以參考下面文章的具體內(nèi)容
    2021-09-09
  • Android使用Handler和Message更新UI

    Android使用Handler和Message更新UI

    這篇文章主要介紹了Android使用Handler和Message更新UI的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-07-07
  • android中intent傳遞list或者對(duì)象的方法

    android中intent傳遞list或者對(duì)象的方法

    這篇文章主要介紹了android中intent傳遞list或者對(duì)象的方法,分析羅列了常用的幾種方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-01-01

最新評(píng)論

年辖:市辖区| 铜陵市| 米泉市| 泾川县| 莱西市| 汤阴县| 瓮安县| 池州市| 丰台区| 金沙县| 封丘县| 西昌市| 通山县| 湖南省| 濮阳县| 南丰县| 醴陵市| 瑞丽市| 营口市| 安吉县| 黎平县| 藁城市| 陈巴尔虎旗| 外汇| 宁夏| 大邑县| 漠河县| 祁连县| 夏河县| 咸阳市| 吐鲁番市| 阳信县| 桐乡市| 海口市| 安仁县| 钟山县| 苍南县| 延寿县| 延庆县| 怀集县| 虹口区|