白話文轉文言文——Kotlin程式碼簡潔之道
這是kotlin
系列文章的第一篇。這個系列記錄的是kotlin
使用感受,其中也會穿插基礎知識點,並通過專案實戰程式碼綜合運用這些知識點。
剛接觸kotlin
就被它的簡潔震撼到了(kotlin
的作者一定是一個極簡主義!)。一起來看下kotlin
是怎麼通過“斷舍離”來實現簡潔的:
new
分號
型別
新建物件不需要new
關鍵詞。
任何語句的結尾不需要;
但加上也不會有語法錯誤。
//java StringBuffer buffer = new StringBuffer(); //kotlin var buffer = StringBuffer() 複製程式碼
-
var
是kotlin
保留字,用於宣告變數。與之對應的是val
用於宣告常量。 -
不需要顯示指明變數型別,因為
kotlin
會根據上下文推斷變數型別,這種能力稱為“型別推導” 。 - 可以通過下面的語法來指定型別:
var buffer: StringBuffer = StringBuffer() 複製程式碼
-
kotlin
中型別是後置的,在變數名後跟上: 型別
就可以顯示指定型別。同理,它也用於指定函式返回值型別:
fun getMessage(): String{ return "message" } 複製程式碼
-
fun
關鍵字用於宣告函式。
implements
extends
@Override
//java public class CheckableActivity extends Activity { final public void setStatus(){} } public class MyListener implements View.OnClickListener{ @Override public void onClick(View v) { } } //kotlin class CirclePartyListActivity : Activity() { fun setStatus(){} } class MyListener : View.OnClickListener{ override fun onClick(v: View?) { } } 複製程式碼
-
kotlin
用:
取代了implements
和extends
保留字。 -
@Override
也被override
保留字取代並且和函式頭同行,kotlin
中的override
是必須的,而java
中是可選的。 -
kotlin
中類和方法預設是final
的(可省略不寫),這意味著預設情況下,類和方法是不允許被繼承和重寫的(這是為了防止脆弱的基類
,即對基類方法的修改會導致子類出現預期之外的行為)。只有通過open
保留字顯示宣告該類或方法可以被繼承或重寫:
open class A{ open fun do(){ } } 複製程式碼
()
kotlin
的lambda
也更加簡約:
//正常情況 view.setOnClickListener({ v -> v.setVisibility(View.INVISIBLE) }) //當lambda是函式的最後一個引數時,可以將其移到括號外面 view.setOnClickListener() { v -> v.setVisibility(View.INVISIBLE) } //當函式只有一個lambda型別的引數,可以去省去括號 view.setOnClickListener { v -> v.setVisibility(View.INVISIBLE) } //當lambda只有一個引數,可省去引數列表,在表示式部分用it引用引數 view.setOnClickListener { it.setVisibility(View.INVISIBLE) } 複製程式碼
getter
setter
java
中,欄位和其訪問器的組合被稱為屬性,kotlin
引入了property access syntax
,它取代了欄位和訪問器方法,用這種方式進一步簡化上面的程式碼:
view.setOnClickListener { it.visibility = View.INVISIBLE } 複製程式碼
-
所有被定義了
getter
和setter
方法的欄位,在kotlin
中都可以通過賦值語法來操作。
{ }
return
kotlin
中的語句和表示式的唯一區別是:表示式有值,而語句沒有。如果函式體中不顯示地指明return
,則程式碼塊中的最後一個表示式的值會被當做函式返回值,此時不需要return
保留字:
//java public int add(int a, int b){ return a+b ; } //kotlin fun add(a: Int, b: Int): Int { a+b } 複製程式碼
如果函式體由單個表示式構成,可以省去花括號和return,並用賦值的=
表示將表示式的值賦值給返回值:
fun add(a: Int, b: Int): Int = a+b 複製程式碼
switch-case-break
//java String color; switch(colorInt){ case Color.RED: color = "red"; break; case Color.BLUE: color = "blue"; break; default: color = "black"; break; } //kotlin val color = when (colorInt) { Color.RED -> "red" Color.BLUE -> "blue" else -> "black" } 複製程式碼
-
when
用於取代switch-case
,不需要在每個分支末尾呼叫break
,如果有一個分支命中則會立即返回。 -
when
是一個表示式,這意味著它有返回值,返回值等於命中分支中最後一條語句的返回值。
default
java
中的default
保留字用於介面中預設方法的實現。在kotlin
中可以省去它。
//java public interface IMessage { default String getMessage() { return "default message"; } int getMessageId(); } //kotlin interface IMessage { fun getMessage(): String { return "default message" } fun getMessageId(): Int } 複製程式碼
-
Int
是java
中基本資料型別int
的包裝類,kotlin
中沒有基本資料型別。
防禦式程式設計
//java public class Address { private String country; public String getCountry() { return country; } } public class Company { private Address address; public Address getAddress() { return address; } } public class Person { private Company company; public String getCountry() { String country = null; //多次防禦式程式設計 if (company != null) { if (company.getAddress() != null) { country = company.getAddress().getCountry(); } } return country; } } //kotlin fun Person.getCountry(): String? { return this.company?.address?.country } 複製程式碼
-
?.
稱為安全呼叫運算子 ,它把判空檢查和一次方法呼叫合併成一個操作。只有當呼叫變數不為null
時,才會執行呼叫,否則整個表示式返回null
。這意味著,不再需要防禦式程式設計。 -
?
置於型別之後表示這個型別可空,上面的函式宣告表示此函式的返回值可能為null
。 -
上面的 kotlin 程式碼為
Person
類添加了一個getCountry()
方法,這種技術叫擴充套件函式 。
擴充套件函式
擴充套件函式是一個類的成員函式,但它定義在類體外面。這樣定義的好處是,可以在任何時候任何地方給類新增功能。
在擴充套件函式中,可以像類的其他成員函式一樣訪問類的屬性和方法(除了被private
和protected
修飾的成員)。還可以通過this
引用類的例項,也可以省略它,把上段程式碼進一步簡化:
fun Person.getCountry(): String? { return company?.address?.country } 複製程式碼
kotlin
預定了很多擴充套件函式,下面就會用到其中的apply
:
冗餘物件名
程式設計中經常會遇到“對同一個物件做多次操作”的場景,比如:
Intent intent = new Intent(this, Activity1.class); intent.setAction("actionA"); Bundle bundle = new Bundle(); bundle.putString("content","hello"); bundle.putString("sender","taylor"); intent.putExtras(bundle); startActivity(intent); 複製程式碼
其中,物件intent
和bundle
重複出現若干次,這對於極簡主義的kotlin
來說不能忍,它的表達方式如下:
Intent(this,Activity1::class.java).apply { action = "actionA" putExtras(Bundle().apply { putString("content","hello") putString("sender","taylor") }) startActivity(this) } 複製程式碼
其中,apply
的定義如下:
//為泛型T物件新增新功能apply(),它接受一個lambda型別的引數block,且lambda呼叫的發起者是物件本身 public inline fun <T> T.apply(block: T.() -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } //執行lambda block() //返回呼叫者自身 return this } 複製程式碼
對於object.apply{lambda}
可以簡單的理解為:在object物件上應用lambda操作,並且最終返回object物件本身。所以上述程式碼也可以寫成更加緊湊的形式:
startActivity(Intent(this, Activity1::class.java).apply { action = "actionA" putExtras(Bundle().apply { putString("content", "hello") putString("sender", "taylor") }) }) 複製程式碼
同一型別的預定義擴充套件函式還包括with
、let
、also
。它們的共同點是適用於“對同一個物件做多次操作”
的場景 。它們的不同點總結如下:
函式 | 返回值 | 呼叫者角色 | 如何引用呼叫者 |
---|---|---|---|
also | 呼叫者本身 | 作為lambda引數 | it |
apply | 呼叫者本身 | 作為lambda接收者 | this |
let | lambda返回值 | 作為lambda引數 | it |
with | lambda返回值 | 作為lambda接收者 | this |
-
kotlin
中,發起呼叫擴充套件函式的那個物件,叫接收者物件 。同理,發起呼叫lambda的物件叫做lambda接收者 。 -
可以將
also
的原始碼和apply
做對比,更好的理解他們呼叫者角色的差別:
//為泛型T物件新增新功能also(),它接受一個lambda型別的引數block,且物件是lambda的引數 public inline fun <T> T.also(block: (T) -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this } 複製程式碼
綜合應用
“讓 app 中所有被點選的 View 都帶上縮放動畫”。綜合運用上述kotlin
知識點實現這個需求之前,先來看看java
是如何實現的:
-
先定義工具類,該工具類為傳入的
View
分別設定觸控和單擊監聽器。在按下時播放動畫,鬆手時反向播放動畫。
public class ViewUtil { public static void addExtraAnimClickListener(View view, ValueAnimator animator, View.OnClickListener listener) { view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: animator.start(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: animator.reverse(); break; } //若返回true,則遮蔽了點選事件 return false; } }); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onClick(v); } } }); } } 複製程式碼
- 在介面中新建動畫和點選響應邏輯並將它們傳遞給工具類
Button btn3 = findViewById(R.id.btn3); //新建動畫:變大 ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 1.2f); animator.setDuration(100); animator.setInterpolator(new AccelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Float value = ((Float) animation.getAnimatedValue()); btn3.setScaleX(value); btn3.setScaleY(value); } }); //點選響應邏輯 View.OnClickListener onClickListener = new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(Activity1.this, "spring anim", Toast.LENGTH_LONG).show(); } }; //應用工具類 ViewUtil.addExtraAnimClickListener(btn3, animator, onClickListener); 複製程式碼
不要眨眼,換kotlin
閃亮登場:
- 給View新增擴充套件函式
//擴充套件函式接收一個動畫和一個點選響應邏輯(用lambda表示) fun View.extraAnimClickListener(animator: ValueAnimator, action: (View) -> Unit) { setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> animator.start() MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> animator.reverse() } false } setOnClickListener { action(this) } } 複製程式碼
- 應用擴充套件函式
btnSpringAnim.extraAnimClickListener(ValueAnimator.ofFloat(1.0f, 1.15f).apply { interpolator = AccelerateInterpolator() duration = 100 addUpdateListener { btnSpringAnim.scaleX = it.animatedValue as Float btnSpringAnim.scaleY = it.animatedValue as Float } }) { Toast.makeText(this, "spring anim", Toast.LENGTH_LONG).show() } 複製程式碼
-
btnSpringAnim
是一個Button
控制元件的id(只要裝了kotlin外掛,就不需要findViewById()) -
as
保留字用於型別強制轉換。 -
是不是有一種“白話文轉文言文”
的感覺,
kotlin
憑藉著極強的表達力用將近 1/3 的程式碼量完成了功能。
知識點總結
-
var
保留詞用於宣告變數,val
保留詞用於宣告常量。大多數情況下不需要顯示指明變數型別,kotlin 具有型別推導能力,會根據上下文自動推斷型別。 -
fun
保留字用於宣告函式。 -
override
保留字表示重寫父類方法或者實現介面中的抽象方法,與 java 不同的是,它必須顯示出現在重寫方法前( java 允許省略)。 -
as
保留字用於型別強制轉換。 -
kotlin 中型別是後置的,在變數名或函式引數列表後跟上
: 型別
就可以顯示指定型別。 -
:
還用於繼承類(取代extends
)、實現介面(取代implements
)。 -
新建物件時不需要
new
,而是直接呼叫建構函式。 -
語句末尾不需要
;
但加上也不會有語法錯誤。 -
kotlin 中類和方法預設是
final
的,他們不能被繼承和重寫。只有通過加上open
後才能被繼承和重寫。 - kotlin 中沒有基本資料型別,而是用其對應的包裝類表示。
-
給介面方法新增預設實現時不需要
default
關鍵字。 - kotlin 中的語句和表示式的唯一區別是:表示式有值,而語句沒有。
- 如果函式體由單個表示式構成,可以省去花括號和return。
-
when
保留字用於去掉switch-case
,而且它是一個表示式,返回值是命中分支中最後一語句的返回值。 -
kotlin 引入了
property access syntax
,不再需要getter和setter方法,可以直接對屬性賦值。 -
?.
稱為安全呼叫運算子 ,只有當呼叫變數不為null
時,才會執行呼叫,否則整個表示式返回null
。這樣就避免了防禦式程式設計。 -
?
置於型別之後表示這個型別的變數或返回值值可能為null
。 - kotlin 使用擴充套件函式,可以在類體外給類新增方法。
-
kotlin 預定了很多擴充套件函式,其中有一類適用於“對同一個物件做多次操作”。包括
also()
、apply()
、let()
、with()
。
最近開始學習 kotlin ,研讀《Kotlin實戰》的同時,在專案中加以運用。這個系列會不斷地新增來自書本和實踐中的新發現。希望對你能有所幫助~~