Kotlin實戰【二】Kotlin基本要素
前言
本章我們將學習怎麼用kotlin宣告任何程式都存在的基本要素:變數、函式、類以及屬性的概念
一、函式和變數
1.1 Hello World
讓我們以一個經典的例子開始:列印“Hello, world!”
fun main(args: Array<String>) {
println("Hello, world!")
}
從上面程式碼我們能看到哪些特點呢?
- 關鍵字fun用來宣告一個函式。(沒錯,kotlin就是這麼fun)
- 引數類寫在引數名字的後面,變數的宣告也是如此。
- 函式可以在檔案的最上層中宣告,你沒必要把它放到一個類中。
- 陣列就是類。不像Java,Kotlin沒有特定的宣告陣列的語法。
- 用println,而不是System.out.println。Kotlin標準庫提供了很多標準Java庫函式的包裝,這有更簡潔的語法。println就是其中之一。
- 和很多現代語言一樣,可以省略每行程式碼結尾的分號。
1.2 函式
1.2.1 函式型別
上面已經看了一個沒有返回值得函式,下面我們看一個有返回值的函式:
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
println(max(1, 2)) //2
我們看到返回型別放在了引數列表之後。
注意:在Kotlin中if是個有返回值的表示式。類似於Java中的三目運算子(a > b)? a : b
函式宣告以fun
開始,函式名緊隨其後,例子中函式名是max
,接下來是引數列表,之後跟著返回型別,之間用冒號隔開。
無返回型別
fun 函式名(引數列表){
函式體
}
有返回型別
fun 函式名(引數列表):返回型別{
函式體
}
語句和表示式 在Kotlin中,if是個表示式,而不是一個語句。語句和表示式的區別在於,表示式是一個值,可以被用作另外表達式的一部分;而語句總是一個包含它的程式碼塊內的頂層元素,沒有自己的值。在Java中,所有的控制結構都是語句,但是在Kotlin中,大部分控制結構,除了迴圈(for , do和do/while),是表示式。聯合控制結構和其他的表示式,可以讓你簡潔表達許多通常的模式。 另外一方面,在Java中賦值是表示式,但是在Kotlin中變成了語句。這有效避免了比較和賦值之間的混淆,這個混淆也是錯誤的一個來源。
1.2.2 表示式函式體
可以讓前面的函式變得更簡單。因為他的函式體是由單個表示式構成,可以用這個表示式作為完整的函式體,並去掉花括號和return
語句:
fun max(a: Int, b: Int): Int = if (a > b) a else b
如果用花括號來表達函式主體,我們叫這個函式為程式碼塊體,如果直接返回表示式,我們叫它為表示式體。 INTELLIJ IDEA提示 : IntelliJ IDEA提供了在兩種不同函式風格“Convert to expression body”和 “Convert to block body”之間的轉換
表示式體的函式在Kotlin程式碼中很常見,不光用在一些簡單的函式中,也用在許多複雜的表示式中,如:if、when、try等,後續介紹
1.2.3 型別推導
我們的max
函式還可以進一步簡化,如下:
fun max(a: Int, b: Int) = if (a > b) a else b
為什麼函式沒有返回型別的宣告呢?作為一個靜態型別語言,Kotlin不是要求每個表示式都應該在編譯期具有型別嗎?事實上,每個變數和表示式都有返回型別。但是對於表示式體的函式,編譯器可以分析作為函式體的表示式,用它的型別作為返回型別,即使沒有顯示的寫出來。分析的這個型別通常叫型別推導(type inference)。
注意:省略返回型別僅僅在表示式體的函式中允許。有程式碼塊體的有返回值的函式,你必須指明返回型別和顯示的返回語句。實際中的函式通常非常長,可能包含很多返回語句,有顯示的返回型別和語句可以幫助你快速的知道什麼被返回。
1.3 變數
在Java中,你用型別宣告變數。但是在Kotlin中,許多變數的型別都可以省略,所以在Kotlin中以關鍵字開始,然後是變數名,最後加上型別(也可以不加)。 省略型別:
val question = "The Ultimate Question of Life, the Universe, and Everything"
val answer = 42
顯示型別:
val answer: Int = 42
不可省略型別: 變數沒有初始化器,需要顯示的指出
val answer:Int
answer = 42
可變變數和不可變數
- val(來源於value)--- 不變的引用。一旦宣告為val的量初始化後,不能夠重新賦值。對應於Java裡面的final變數
- var(來源於variable)--- 可變的引用。變數的值可以改變。對應於Java裡面的正常的變數(非final)
通常,儘量宣告所有的變數為val關鍵詞。只有有需要的時候,才變為var。使用不可變引用、不可變物件及無無副作用的函式讓你的程式碼更接近函數語言程式設計風格。
定義了val變數的程式碼塊執行期間,val變數只能進行唯一一次初始化。但是,如果編譯器能確保只有唯一一條初始化語句被執行,可以根據條件使用不同的值來初始化它:
val message: String
if (canPerformOperation()) {
message = "Success"
// ... perform the operation }
else {
message = "Failed"
}
注意:儘管val引用自身是不可變得,但是它指向的的物件可能是可變的:
val languages = arrayListOf("Java") //宣告不可變的引用
languages.add("Kotlin")//改變引用指向的例項
注意:儘管var關鍵詞允許變數改變他的值,但是它的型別是確定的:
var answer = 42
answer = "no answer"//編譯錯誤:型別不匹配
編譯器只會根據初始化器來推斷變數的型別,在決定型別的時候不會考慮後續的賦值操作。
如果你想在變數裡面儲存一個不匹配的型別的值,你必須轉換或者協變這個值到正確的型別。
1.4 更容易的字串格式化:字串模板
fun main(args: Array<String>) {
//列印“Hello,Kotlin”,如果輸入引數為Bob,則列印“Hello,Bob”
val name = if (args.size > 0) args[0] else "Kotlin"
println("Hello, $name!")
}
這個例子引進了一個功能叫字串模板(string templates)。和其他指令碼語言一樣,Kotlin允許在字串字面量中,通過$字元放在變數名前面,引用本地變數。這個同Java中的字串連線("Hello, " + name + "!"), 但是更加緊湊和有效率(注:都是建立StringBuilder,新增常量部分和變數值,Java虛擬機器有優化)。
如果你引用一個不存在的本地變數,因為表示式會靜態檢查,這些程式碼會編譯不成功。如果你想在字串中包含
x,而不是把x翻譯為一個變數的引用。
不限於一個簡單的變數名,你也可以用更加複雜的表示式,僅僅只要在表示式括上花括號:
fun main(args: Array<String>) {
//用${}插入args陣列的第一個元素
if (args.size > 0) { println("Hello, ${args[0]}!") }
}
你也可以雙引號內陷雙引號,只要他們是在同一個表示式:
fun main(args: Array<String>) {
println("Hello, ${if (args.size > 0) args[0] else "someone"}!")
}
二、類和屬性
面向物件程式設計可能不是什麼新鮮話題,Kotlin這方面也似曾相識,但是你會發現許多常見的任務使用更少的程式碼就可以完成。
讓我們看看一個簡單的JavaBean的Person類,現在只包含一個name屬性:
/* Java */
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
在Java中,構造方法的方法體常常包含重複內容,把引數賦值給有著相同名稱的欄位。在Kotlin中,這個邏輯不需要如此多的樣板程式碼。
/* Kotlin*/
class Person(val name: String)
這種類(只有資料沒有其他程式碼)通常被叫做值物件
注意:從java到Kotlin的轉換過程中public修飾符消失了。在Kotlin中public是預設的可見性。
2.1 屬性
- 在java中,如果你想讓類的使用者訪問到資料,需要提供訪問方法:一個getter、可能有一個setter,setter可能包含一些額外的邏輯,驗證傳遞值,或者傳送值變化的通知等等。
- 但是在Koltin中,屬性是頭等的語言特信,完全替代欄位和訪器方法。使用val和var關鍵字。宣告val的屬性只讀,var是可變的
class Person(
val name: String, //只讀屬性:自動生成一個域和簡單的getter
var isMarried: Boolean //可寫屬性:一個域,getter和setter
)
接下來我們看下如何使用上面定義好的Person類:
val person = Person("Bob", true)
println(person.name)// Bob
println(person.isMarried) //true
現在可以直接引用屬性,不再需要getter,邏輯沒變,程式碼更加簡潔。
小貼士:
- 你可以在Java定義的類中使用Kotlin的屬性語法。在Java類中的getter可以在Kotlin中val屬性獲取,getter/setter可以通過var屬性獲取。比如,如果在Java類定義了setName和setName的方法,那麼可以通過叫name的屬性獲取。如果類定義了isMarried和setMarried方法,相應的Kotlin屬性叫isMarried。
2.2 自定義屬性訪問器
這個部分,你將看到怎麼自定義實現一個屬性訪問器。假設你聲明瞭一個長方形,它可以告訴是不是一個正方形。你沒必要用單獨的域儲存這個資訊,因為你需要動態檢查高是否等於寬:
class Rectangle(val height: Int, val width: Int) {
val isSquare: Boolean
get() { //Property getter declaration
return height == width
}
}
isSquere屬性不需要一個域來儲存它的值。它僅僅是自定義實現的getter。
val rectangle = Rectangle(41, 43)
println(rectangle.isSquare) //false
2.3 Kotlin原始碼佈局:目錄和包
Java把所有的類放進包裡面。Kotlin也像Java,有包的概念。每個Kotlin檔案在開頭有package語句,檔案中所有的宣告(類、函式和屬性)將放在這個包下。如果其他的檔案在同一包下,裡面所有的定義可以直接使用;如果這些定義在不同包裡面,那麼他們需要匯入。就像在Java中,匯入語句放置在檔案的開頭,使用import關鍵詞。下面是個例子,展示包宣告和匯入語句:
package geometry.shapes //包宣告
import java.util.Random //匯入標準Java庫類
class Rectangle(val height: Int, val width: Int) {
val isSquare: Boolean
get() = height == width
}
fun createRandomRectangle(): Rectangle {
val random = Random()
return Rectangle(random.nextInt(), random.nextInt())
}
接下來看一下java和kotlin的目錄結構
如上圖:java中,目錄層級結構照搬了包層級結構
如上圖:kotlin中,不需要遵循目錄層級結構
在kotlin中,可以把多個類放在同一個檔案中,檔案的名字還可以隨意選擇。
但是,在大多數情況下,跟隨Java目錄結構和根據包結構把原始碼組織成目錄,是最佳實踐。特別是Kotlin和Java混合的專案,堅持這樣的結構特別重要。因為這樣做可以讓你逐步遷移程式碼,而沒有引入意外的情況。 但是當類很小的時候(在Kotlin中,這些經常存在)。請你不要猶豫把多個類合成到同一個檔案。
總結
- fun關鍵字用來宣告函式。val關鍵字和var關鍵字分別用來宣告只讀變數和可變變數
- 字串模板幫助你避免繁瑣的字串拼接。在字串前加上{}包圍一個表示式,來把值注入到字串中。
- 實體類(值物件類)在Kotlin中以更簡單的方式表示。
- 在kotlin中,可以把多個類放在同一個檔案中,檔案的名字還可以隨意選擇。
文章轉自 https://juejin.cn/post/6844903624833761293 如有侵權,請聯絡刪除。
相關視訊推薦:
Android 效能優化學習【二】:APP啟動速度優化_嗶哩嗶哩_bilibili
【Android進階系統學習】:位元組碼插樁技術實現自動化方法耗時記錄_嗶哩嗶哩_bilibili
Android 效能優化學習【二】:APP啟動速度優化_嗶哩嗶哩_bilibili
【Android面試專題】:面試又被問到程序間通訊,你卻連Binder是什麼都不知道?_嗶哩嗶哩_bilibili
BAT面試技巧——Android面試必問的網路程式設計你瞭解多少?_嗶哩嗶哩_bilibili