作者:kotlon
連結:
https://www.jianshu.com/p/dcf6cd7c59a3
本文由作者授權發布。
kotlin 在 17 年 google io 大會上確定為親兒子,android studio canary 3.0 版本開始,直接支持 kotlin 語言,不需要額外安裝 as plugin。
kotlin 的出現,給 android 開發者帶來了極大的活力,現在的 android 工業開發,講究的是語法糖,效率,性能,高質量,以及可拓展性,而 kotlin 的出現給 java 開發者帶來了極大的福音。
kotlin 官網教程英文
https://kotlinlang.org/docs/reference/basic-types.html
kotlin 官網教程中文
https://www.kotlincn.net/docs/reference/
閱讀此文章大約半小時~
如何閱讀?
瀏覽kotlin 優勢和kotlin 坑這兩大章節,然後去學習 kotlin 基礎語法,根據第三章,kotlin 普及裡的建議,可以在實際開發中,開幹了。
避免 npl以及非空判斷更優雅kotlin 默認是非空的,如果你需要聲明一個可能為空的變量,那麼如下:
private var mName:String = "kk"
private var mSubName:String? = null
這裡的 mName 變量聲明為,String 類型;mSubName 聲明為 String?。前者不能為 null,後者可以為 null。
然後,你可能經常在 java 中寫這樣的代碼:
if (mANRPA != null) {
mANRPA.stop();
}
那麼在 kotlin 你這要這樣寫就行了:
使用 ?. 的效果和上面的 java 代碼是一樣的,表示該變量可能為空,如果不為空則執行後面的表達式。
參考地址:
https://www.kotlincn.net/docs/reference/null-safety.html
java 語法完全兼容 kotlin
你可以通過幾個小時學習 kotlin 的基礎語法,包括:
怎麼定義變量
怎麼定義方法
怎麼定義類
等等.
然後就可以像 java 一樣使用 kotlin,當然這不是我們的最終目的,因為 kotlin 的語法糖才是我們最後的目標。
google 發布了 kotlin 簡易教程,大概只需要幾個小時,就可以看完:
在實際的開發中,大概第一個星期內,你寫代碼的速度會下降一些,但是一個星期之後,完全上手了,寫代碼的速度是有很大的提升的。
1.extension-擴展函數和擴展屬性關於 擴展函數的說明:
https://www.kotlincn.net/docs/reference/extensions.html
android ktx 庫提供了一系列的優秀的擴展函數 官方說明
https://android.github.io/android-ktx/core-ktx/index.html
簡單的說,擴展函數是什麼呢?先來看一個例子,在java 裡面 你要 remove 一個 View 的 Parent,你可能會這樣寫:
if(mLoadingView.getParent() != null){
((ViewGroup)mLoadingView.getParent()).removeView(mLoadingView);
}
但是每次都這樣寫,貌似比較重複,對吧;然後你就可能考慮寫一個靜態方法,類似如下,比如在 ViewUtils 類似命名裡寫一個靜態函數:
public static void removeSelf(View view){
if(view == null) return;
if(view.getParent() != null){
((ViewGroup)view.getParent()).removeView(view);
}
}
但是在 kotlin 中,使用擴展函數,可以更巧妙更直接的實現這個功能。
寫在某個文件裡面,編寫一個頂級的擴展函數,如下:
inline fun View.removeSelf(): Boolean {
if (this.parent != null && this.parent is ViewGroup) {
(this.parent as ViewGroup).removeView(this)
return true
}
return false
}
然後在需要調用的地方,你直接這樣寫就行了:
當然 kotlin 擴展函數的作用並不僅僅在此,它的思想主要是豐富實際類的語法。
例如,現在讓你設計一個功能,點擊一個按鈕,然後發起一個網絡請求,但是需要注意在 View 的生命周期內,如果View 銷毀了,取消這個網絡請求,並且不會更新UI。那麼常規的 java 代碼,寫起來可能就是一堆的回調,但是使用 kotlin 的 擴展函數+代理,封裝之後,你看到的就是下面一行代碼。
mJumpBitmap.onClickAutoDisposable {
}
代理在設計模式上是非常優秀的,例如 retrofit 框架裡,大量的使用動態代理這種設計模式;然後 kotlin 在語法層面去支持了代理,包括代理和代理屬性。
代理屬性的話,kotlin 支持一些標準的代理屬性,例如 by lazy,Observable,Storing;其中 by lazy,提供三種類型的懶加載,包括非線程安全,synchronized 同步線程安全,以及cas 操作線程安全。
示例如下,例如在 PushProto.java 中,需要對 ProtoBuf 解析單例進行初始化,並且要求是線程安全的,原先 java 代碼如下,也就是一個 double check 的單例模式。
private static volatile com.google.protobuf.Parser<PushProto> PARSER;
if (PARSER == null) { synchronized (com.yy.hiyo.proto.PushProto.class) {
if (PARSER == null) {
PARSER = new DefaultInstanceBasedParser(DEFAULT_INSTANCE);
}
}
如果是用 kotin 則只需要使用 by lazy 懶加載委託屬性便能實現類似的效果,kotlin 代碼如下:
val initProtoInstance by lazy<DefaultInstanceBasedParser> {
DefaultInstanceBasedParser();
}
注意:實際上這裡使用的並非是是 double check 的模式,而是類似的一種線程安全模式,源碼如下:
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
@kotlin.jvm.JvmVersion
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
}
else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
}
by lazy 的三種模式如下:
LazyThreadSafetyMode.SYNCHRONIZED 類似 double check 線程安全,synchronized 悲觀鎖
LazyThreadSafetyMode.PUBLICATION 線程安全,使用 cas 鎖,多個線程可以同時執行初始化代碼塊,但是只返回第一個執行完成的數值,作為初始化
LazyThreadSafetyMode.NONE 單線程先可用,等於 java 懶漢式單例
其後,其它兩種標準屬性,參考官方教程。
3.語法層面支持懶加載-by lazy 和 lateinit懶加載是經常使用的一個功能,傳統的 java 懶加載,可能是在使用的是判斷一下對象是否為空,但是在 kotlin 裡,提供了語法層次的懶加載,表示該變量在使用之前一定會被初始化。
by-lazy 上面說到了,lateinit 簡單用法如下:
lateinit var mStirng: String
mString = "some"
if(TestActivity::mStirng.isLateinit){
}
在 java 中,泛型和反射都是使用頻率極高的語法,使用泛型或者接口編程之後,我們經常要使用 instance of 做判斷,如下代碼:
if (instance instanceof BasePresenter) {
((BasePresenter) instance).onInit(mMvpContext);
}
在kotlin 裡面,只需要寫如下代碼:
fun checkSting(content:Any){
if(content is String){
content.substring(1)
}
}
在泛型和接口編程使用廣泛的情況下,kotlin 的類型安全存在以下優勢:
is 操作符,支持非操作,比 java 更簡潔,在語法層次表面的 非該類型;其次 is 類型判斷符,作用域類,類型會智能轉換,也就是例子中說的。需要注意是,val 和 var 類型,支持的 is 操作不一樣。
as 操作符,支持空類型轉換,也就是 as?,可以轉換為 String? 等可空類型,類似 java 操作是 先要判斷該對象是否為該類型,然後強轉,語法比較繞,然而 kotlin 直接支持可為空類型的轉換
在 kotlin 裡面函數是第一公民,java 8 也把加入了該特性,但是遠遠沒有 kotlin 的函數功能強大,kotlin 的函數功能如下:
參數值和變量可以是函數
函數參數支持默認參數
支持命名參數
支持 lambda
支持內聯函數
支持強大的擴展函數
編譯器支持內聯優化
函數可以有一個接收者
函數和 lambdas 表達式對實際開發帶來極大的方便,也豐富了編程思想,更多的細節,可以參考官方教程。
6.強大的集合功能在 kotlin stdlib 裡面,支持了強大的集合功能,支持各種高階函數,主要的高階函數如下:
具體可以參考一個專欄 kotlin 學習之路
https://zhuanlan.zhihu.com/LearningKotlin
或者可以直接在 AS 裡面搜索類 _Collection.kt,裡面可以看到該類的所有的高階函數.
下面是一個簡單的示例,比如你在 java 中需要對一個後端返回的 List 進行排序,java 中常見的實現方案有
1. Collections 中static <T extends Comparable<? super T>> void sort(List<T> list)傳入該 List,但是 List 中的元素需要實現 Comarable 接口,並且重寫 compareTo()方法
2. Collection中,static <T> void sort(List<T> list, Comparator<? super T> c) 入該 List 和 一個Comparator。
3. java 中,支持 lambda 表達式之後,你可以這樣寫
ballList.sort(Comparator.comparing(Ball::getHits));
表示使用 Ball 的 hits 欄位進行比較
4. java 8 中可以使用 stream
如果使用 kotlin,這裡支持各種各樣的sort() 方式,如下 api:
那麼上面的例子中,kotlin 中這個排序只要使用 Collections 裡面的擴展函數就可以做了。
ballList.sortByDescending {it.hits}
所以其實寫到這裡,我們已經見識到了,kotlin 的開發者幫助我們考慮了各種語法場景,我們站立在一個偉大的開發團隊上,自然而然能更快的寫出更高質量的代碼。
Kotlin的標準庫提供了一系列提高開發效率的函數,例如 apply{} ,run{},let{} 等,這裡的解釋都比較簡單,具體自己看一下 api 就行了,位於 Standard.kt 文件中,使用例子如下:
EnterRoomConfig.RoomlistEventBean eventBean = new EnterRoomConfig.RoomlistEventBean();
eventBean.setColumn(String.valueOf(position + 1));
eventBean.setRow(baseRoomEventListBean.getRow());
eventBean.setPositionId(String.valueOf(position + 1));
eventBean.setToken(token);
eventBean.setModelId(baseRoomEventListBean.getModelId());
你可能不斷的為訪問 java bean 或者某個對象的屬性或者方法,則需要不停的寫 對象實例.屬性 或者 對象實例.方法。在 kotlin 中,你完全可以釋放自己的雙手,如下:
val enterRoomConfig = EnterRoomConfig("").apply {
//這裡有個 this 指向前面new 出來的 EnterRoomConfig() 實例
//其實等於 this.isGuideEnter
isGuideEnter = false
isQuickMatch = true
followUid = followUid
pwd_token = ""
}
apply() 的源碼如下:
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
這裡包含,contract 用法和 帶接受者的函數,block() 函數的接收者是T,kotlin 這裡其實是提供了一種方便的建造者模式,在 java 裡面,你實現建造者模式,需要自己手動去編寫接口和 Builder 實現,在 kotlin 則使用這些高階函數,方便你隨意的實現 Builder 模式。
8.編寫單例更快了你只需要使用 Object 就可以編寫單例了,如下:
object CloUtils {
fun test(){
}
}
那麼kotlin 上的單例和 java 的各種單例模式,性能上有什麼區別嗎?kotlin 使用 object 關鍵字聲明的單例,翻譯成 java 如下:
public final class CloUtils {
public static final CloUtils INSTANCE;
public final void test() {
}
static {
CloUtils var0 = new CloUtils();
INSTANCE = var0;
}
}
也就是 java 常見的類似 餓漢式的單例,這種單例是線程安全的,可能存在一定問題:
1. 在類加載的時候,單例會被初始化,所以不要在初始化代碼塊,做太多操作
2. 不能直接在構造函數傳遞參數,其實是 object 不能夠有一個 constractor
那麼實際上我們可以使用其它方式實現我們需要的效果,例如 懶漢式和 double check 等模式,類似 by lazy。
9.kotlin 和 android-android extension插件實際上,kotlin 是一門全棧的語言,可以寫基於 jvm 的,例如 android 和 後端,也可以寫前端,但是在 android 上的表現,可能是最亮眼的,如是說:
在某個 module 的 gradle 文件中:
apply plugin: 'kotlin-android-extensions'
然後你就可以使用 android extension 全家桶了,可以幫你做啥呢?
View Binding 取代繁瑣的 findViewById()方法
例如某段 java 代碼:
//數據域聲明各種View
private YYTextView titleTv, roomNameTv, onlineTv, tagTv;
private YYImageView moreIv, lockIv;
private YYLinearLayout titleLayout;
private ViewGroup onlineCountLayout;
private BubblePopupWindow mPopupWindow;
//然後各種 findViewByID
titleTv = findViewById(R.id.tv_title_when_popup);
titleLayout = findViewById(R.id.layout_title_main);
roomNameTv = findViewById(R.id.tv_room_name);
onlineTv = findViewById(R.id.tv_online_count);
tagTv = findViewById(R.id.tv_tag);
moreIv = findViewById(R.id.iv_more);
lockIv = findViewById(R.id.iv_lock);
onlineTv.setTypeface(FontUtils.getTypeFace(FontUtils.FontType.DINMittelschriftAlternate));
roomNameTv.setOnClickListener(this);
tagTv.setOnClickListener(this);
findViewById(R.id.iv_back).setOnClickListener(this);
moreIv.setOnClickListener(this);
在 kotlin 中,你使用 extension 插件,就可以在這樣訪問控制項:
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
val tag = "MainActivity"
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initBut()
}
private fun initBut(){
mJumpBitmap.setOnClickListener {
val intent = Intent(this@MainActivity, BitmapAcitivty::class.java)
startActivity(intent)
}
mJumpFresco.setOnClickListener {
val intent = Intent(this@MainActivity, FrescoRecyvlerActivity::class.java)
startActivity(intent)
}
}
原理很簡單,就是 extension 插件幫你 findViewById 了類似其它注入框架。
這裡有幾點注意的,你可以在 Activity 或者 Fragment 或者自定義View 或者 ViewHolder 裡面使用這個特性,無需任何其它操作。其次 這個是有Cache 的,每次直接訪問id,並不會每次都去 findViewById,只要第一次才會 findViewByID,然後後面會存起來。
使用註解實現 Pracelable
在實際開發中,你可以給一個類實現系列化,但是中途可能增加欄位,傳統的 java 編寫中,或者使用 ide 插件編寫,增加欄位也是非常辛苦,需要在 writeToParcel() 和 createFromParcel() 方法添加代碼,如果使用 extension 插件,只需要開啟這個設置:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
..
androidExtensions{
experimental = true
}
}
然後在實現 Parcelable 的類上增加一個註解 @Parcelize,代碼如下:
@Parcelize
data class Ball(var hits: Int) : Parcelable {
}
實際上,gradle 插件會為你自動加上 Parcelable 的實現。
還有其它 experimental 的特性等待你去挖掘
參考文檔:https://kotlinlang.org/docs/tutorials/android-plugin.html
kotlin Coroutine -kotlin 協程庫Coroutine 協程,是kotlin 上的一個輕量級的線程庫,對比 java 的 Executor,主要有以下特點:
更輕量級的 api 實現協程
async 和 await 不作為標準庫的一部分
suspend 函數,也就是掛起函數是比 java future 和 promise 更安全並且更容易使用
那麼實際本質上和線程池有什麼區別呢?我的理解是這樣的,協程是在用戶態對線程進行管理的,不同於線程池,協程進一步管理了不同協程切換的上下文,協程間的通信,協程掛起,對於線程掛起,粒度更小,而且一般不會直接佔用到CPU 資源,所以在編程發展的過程中,廣義上可以認為 多進程->多線程->協程。
協程並不會映射成內核線程或者其他這麼重的資源,它的調度在用戶態就可以搞定,任務之間的調度並非搶佔式,而是協作式的。
協程庫的強大,不言而喻,這裡是我之前寫過的關於協程庫的分享,文檔連結:
https://www.jianshu.com/p/c531eefef4c6
Kotlin -anko 在代碼中使用 dsl 編寫布局anko 庫,你可以在在代碼中編寫布局,如下:
verticalLayout {
backgroundColor = Color.parseColor("#eeeeee")
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)
}
同樣的 anko 這邊有個插件,可以支持實時預覽-你需要在編寫代碼之後,build 一下,然後就可以預覽布局。
由於anko 存在預覽問題,並且層級嵌套的時候,不好優化,所以暫時不建議使用 anko 去編寫布局.
對於 java 的程式設計師,kotlin 開發者提供了一種方式,幫助開發者去理解 kotlin,也就是可以把 kotlin 編譯出來的字節碼,反翻譯成 java 代碼,具體操作 tools-show kotlin bytecod-然後在右側會看到 kotlin 文件編譯之後的字節碼文件-然後點擊Decompile ,接著就可以看到 kotlin "翻譯出"java 的文件,之前講述 編寫單例模塊裡面,就是一個例子。
舊的 java 代碼怎麼辦一段時間之後,大家寫 kotlin 都寫得很爽了,那麼總是有一個舊的代碼是用 java 寫的,那怎麼辦?如果你想把 java 改成 kotlin,也很簡單:
選中你的 java 文件,點擊 AS 導航欄 code-covert Java File To Kotlin File,這樣就可以轉過去了,然後進行簡單的修改,也就是把一些報錯和 waring 去掉(一般不會有報錯)。轉換之後的文件可以完美的運行的,這個你不用擔心。
當然 在使用 kotlin 過程中,會發現一些問題或者成本。
成本問題對個人而言:
第一周使用 kotlin 在你的工作中,你會覺得效率下降,而且有點不知所措,甚至有點抵抗 kotlin,但是在兩三個星期之後,你一定是這樣的:
對於團隊而言
針對問題二,kotlin 的開發者正在優化,同時我們可以在構建的時候,監聽構建task 花費的時間,讓數據去評測,是否值得引入 kotlin。
選取了某個項目中的 module ,該 module 的基本為 kotlin 代碼編寫,使用 kotlin 構建時間增加了:
1% :app:kaptGener…ReleaseKotlin (0:07.775)
1% :app:kaptReleaseKotlin (0:05.872)
▇ 2% :app:compileReleaseKotlin (0:21.167)
如果是 java 代碼的話,上述的時間可能是一半(猜測是這樣的),但是從實際數據看,該 module 有幾百個 kotlin 文件,編譯所花費的時間,也不過是增加了十幾秒,所以並不是特別大的問題。
1.java null 問題習慣寫 java 的同學,一般是不會寫這樣的聲明的,去聲明一個參數是可為空的:
fun printSome(content:String?){
}
一般你是這樣聲明的:
fun printSome1(content:String){
}
在 kotlin 裡面,是沒問題的,即便是你傳入 null,編譯器也會提示你,不可以傳入null,但是一旦在 java 去調用這個 printSome1() 方法,一旦你傳入了 null,如下:
CloUtils.INSTANCE.printSome1(null);
依舊是ok的,但是一旦運行的時候,就會報錯:
2019-05-05 11:09:05.705 11674-11674/com.yy.yylite.kotlinshare E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.yy.yylite.kotlinshare, PID: 11674
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.yy.yylite.kotlinshare/com.yy.yylite.kotlinshare.bitmapcompress.BitmapAcitivty}: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter content
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2740)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2801)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1548)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6368)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791)
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter content
at com.yy.yylite.kotlinshare.collectionutils.CloUtils.printSome1(CloUtils.kt)
at com.yy.yylite.kotlinshare.flow.ff.testJavaCallKotlin(ff.java:31)
at com.yy.yylite.kotlinshare.bitmapcompress.BitmapAcitivty.onCreate(BitmapAcitivty.kt:29)
at android.app.Activity.performCreate(Activity.java:6861)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2801)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1548)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6368)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791)
具體的原因在於,編譯器層會幫我們加這樣一行代碼:
Intrinsics.checkParameterIsNotNull(content, "content");
如果傳入的參數為 null,則會直接拋出一個異常。
同樣的,在 kotlin 裡面,如果執行這樣的代碼:
var nonNullStr:String = null
在之前某次迭代中,A 同學寫了這樣一行代碼:
val gson = Gson()
fastSpeech!!.words = gson.fromJson(words, object : TypeToken<List<String>>() .type)
hotWordPopupWindow?.setData(fastSpeech!!.words, fastSpeech!!.classId)
....
作為一個老道的程式設計師都知道,使用 GSON 的方法,沒有 try catch 是不安全的,何況 fromJson 方法籤名就有 throw 異常的,如下:
public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {}
然而你使用 java 的時候,編譯器會提示你需要 catch 異常,但是在 kotlin 裡面,確實不會有的。
原因是因為 kotlin 開發者,認為所有的 Exception 都是 unchecked 的,編譯器不會提示你 catch 住,但是在過去的 java 開發裡面,effect java 認為,處理 checked Exception 是優雅合理的;
原文如下:
Checked Exceptions
In Kotlin, all exceptions are unchecked, meaning that the compiler does not force you to catch any of them. So, when you call a Java method that declares a checked exception, Kotlin does not force you to do anything。
在過去的實踐裡發現,在處理 json,網絡,IO 的時候,需要開發者關注,是否需要 try catch,當然了,這可能也是一個好處,畢竟寫代碼的人關心的細節更多了。
關於 java 和 kotlin 互調參考
https://kotlinlang.org/docs/reference/java-interop.html
需要團隊成員去學習官網教程 或者 google 提供的快速學習的教程,當然這裡只是快速瀏覽就行了,主要還是得靠實踐,先熟悉基本的語法,然後找時間對語法糖進行了解,最後在實踐開發中使用高階特性。
官網教程-中文
https://www.kotlincn.net/docs/reference/
官網教程-英文
https://kotlinlang.org/docs/reference/basic-types.html
快速入門教程
幾小時上手 kotlin
然後可以在一段時間內需要通過 review 代碼,提高大家對 kotlin 的認識和用法,大概持續一個開發周期,之後基本可以很順暢的寫代碼了。
最後就是規則~怎麼讓 java 和 kotlin 共存
最後就是 IDE 對 kotlin 的友好支持,就是你在 commit 的時候,會對代碼進行一個 增量的 code analyse,然後虎會給出你一個更好的建議,其中就包括 kotlin 語法的糾正。如下展示:
然後點擊 commit 之後,點擊review
最後 IDE 會給出更 合理的代碼提示,一般可以改的儘量改一下,如下,這裡提示這一行命名是多餘的:
最後推薦一下我做的網站,玩Android: wanandroid.com ,包含詳盡的知識體系、好用的工具,還有本公眾號文章合集,歡迎體驗和收藏!
推薦閱讀:
J 神的ButterKnife竟然還隱藏著這樣的黑科技?
掃一掃 關注我的公眾號
如果你想要跟大家分享你的文章,歡迎投稿~
┏(^0^)┛明天見!