這是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,並用賦值的=
表示將表示式的值賦值給返回值:
//java
public int add(int a, int b){
return a+b ;
}
//kotlin
fun add(a: Int, b: Int): Int = a+b
複製程式碼
在 lambda 表示式中包含多條語句或表示式時,若省略return
,則預設將最後一個表示式的值作為返回值:
view.setOnTouchListener { v, event ->
...//do something
false
}
複製程式碼
上述程式碼表示在OnTouchListener.onTouch()
中返回 false。
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實戰》的同時,在專案中加以運用。這個系列會不斷地新增來自書本和實踐中的新發現。希望對你能有所幫助~~