博客主頁java
對比下Java的類Person與Kotlin的類Person區別:git
// java public class Person { private final String name; public Person(String name) { this.name = name; } // 提供一個getter訪問器 public String getName() { return name; } } // kotlin // 在Kotlin中,public是默認的可見性,能夠省略 class Person(val name: String)
在Kotlin中,在類中聲明一個屬性和聲明一個變量同樣,使用 val(只讀的) 和 var(可變的) 關鍵字。正則表達式
若是屬性的名稱以 is 開頭,getter不會增長任何的前綴,而它的setter名稱中的is會被替換成set。segmentfault
class Person( // 只讀屬性,生成一個字段和一個簡單的getter val name: String, // 可寫屬性,一個字段,一個getter和一個setter var address: String, var isMarried: Boolean ) // kotlin轉換爲java public final class Person { @NotNull private final String name; @NotNull private String address; private boolean isMarried; @NotNull public final String getName() { return this.name; } @NotNull public final String getAddress() { return this.address; } public final boolean isMarried() { return this.isMarried; } public final void setMarried(boolean var1) { this.isMarried = var1; } public final void setAddress(@NotNull String var1) { this.address = var1; } public Person(@NotNull String name, @NotNull String address, boolean isMarried) { this.name = name; this.address = address; this.isMarried = isMarried; } }
在java 和 kotlin 中調用Person類區別:數組
// java中使用Person Person person = new Person("kerwin", "anqing", false); person.setMarried(true); System.out.println(person.getName() + " : " + person.getAddress() + " : " + person.isMarried()); // kerwin : anqing : true // kotlin中使用Person val person = Person("kerwin", "anqing", false) person.isMarried = true println("${person.name} : ${person.address} : ${person.isMarried}") // kerwin : anqing : true
下面自定義一個屬性isSquare,實現getter,它的值是每次訪問屬性的時計算出來的。app
// kotlin class Rectangle( val height: Int, val width: Int ) { // 自定義訪問器 val isSquare: Boolean // 聲明屬性的getter // 也能夠這樣寫: get() = width == height get() { return width == height } } // kotlin轉java public final class Rectangle { private final int height; private final int width; public final boolean isSquare() { return this.width == this.height; } public final int getHeight() { return this.height; } public final int getWidth() { return this.width; } public Rectangle(int height, int width) { this.height = height; this.width = width; } }
每個kotlin文件都能以一條package語句開頭,而文件中定義的全部的聲明(類、函數、屬性)都會被放到這個包中。若是包不相同,則須要導入它們,使用關鍵字import函數
kotlin不區分導入的是類仍是函數,且容許使用import關鍵字導入任何種類的聲明。可直接導入頂層函數的名稱工具
// 包聲明 package com.kerwin.kotlin.demo class Rectangle( val height: Int, val width: Int ) { val isSquare: Boolean get() = width == height } // 在com.kerwin.kotlin.demo包中定義函數 fun createRectangle(): Rectangle { return Rectangle(12, 23) }
導入其餘包中的函數佈局
// 包聲明 package com.kerwin.kotlin.demo1 // 導入函數名稱 import com.kerwin.kotlin.demo.createRectangle fun main(args: Array<String>) { println(createRectangle().isSquare) }
在kotlin中,能夠把多個類放在同一個文件中,文件的名字還能夠隨意選擇。kotlin也沒有對磁盤上源文件的佈局強加任何限制。包層級結構不須要遵循目錄層級結構。post
kotlin聲明一個枚舉類,使用 enum class 關鍵字。枚舉類中定義任何方法,就要使用分號(;)把枚舉常量列表和方法定義分開。
// 聲明一個帶有屬性的枚舉類 enum class Color( // 聲明枚舉常量的屬性 val r: Int, val g: Int, val b: Int ) { // 在每一個常量建立時指定屬性值 RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); // 必需要有分號 // 給枚舉類定義一個方法 fun rgb() = (r * 256 + g) * 256 + b }
when 是一個有返回值的表達式。
// kotlin中不須要在每一個分支都寫上break語句。若是匹配成功。只有對應的分支會執行 fun getColorString(color: Color) = when(color) { Color.RED -> "red" Color.BLUE -> "blue" Color.ORANGE -> "orange" Color.YELLOW -> "yellow" Color.GREEN -> "green" }
能夠把多個值合併到同一個分支,只須要用逗號(,)隔開這些值就能夠
fun getWarmth(color: Color) = when(color) { Color.RED, Color.ORANGE, Color.YELLOW -> "warm" Color.BLUE, Color.GREEN -> "cold" }
when 容許使用任何對象做爲分支條件。
// 在when分支中使用不一樣的對象 fun mix(c1: Color, c2: Color) = // when 表達式的實參能夠是任何對象,它被檢查是否與分支條件相等 when (setOf(c1, c2)) { setOf(Color.RED, Color.YELLOW) -> Color.ORANGE setOf(Color.YELLOW, Color.BLUE) -> Color.GREEN else -> throw Exception("dirty color") }
若是沒有給when表達式提供參數,分支條件就是任意的布爾表達式.
fun mixOptimized(c1: Color, c2: Color) = when { (c1 == Color.RED && c2 == Color.YELLOW) || (c1 == Color.YELLOW && c2 == Color.RED) -> Color.ORANGE (c1 == Color.YELLOW && c2 == Color.BLUE) || (c1 == Color.BLUE && c2 == Color.YELLOW) -> Color.GREEN else -> throw Exception("dirty color") }
定義一個函數:(1 + 2) + 4 算術表達式求值。智能轉換隻在變量通過is檢查後再也不發生變化的狀況下有效。屬性必須是一個val屬性,且不能有自定義的訪問器。使用as關鍵字來表示到特定類型的顯示轉換。
// 僅做爲一個標記接口 interface Expr // 實現接口使用冒號(:),後面跟上接口名稱 class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr // 使用if層疊對錶達式求值 fun eval(e: Expr): Int { // 使用is檢查來判斷一個變量是不是某種類型 if (e is Num) { // 顯示的轉換成類型Num是多餘的 val num = e as Num return num.value } if (e is Sum) { // 變量e智能轉換了類型 return eval(e.left) + eval(e.right) } throw IllegalArgumentException("Unknown") } >>> println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
kotlin沒有三元運算符,由於if表達式有返回值。若是if分支只有一個表達式,花括號能夠省略。若是if分支是一個代碼塊,代碼塊中的最後一個表達式會被做爲結果返回。
// 使用有返回值的if表達式 fun eval(e: Expr): Int { if (e is Num) { e.value } else if (e is Sum) { eval(e.left) + eval(e.right) } else { throw IllegalArgumentException("Unknown") } }
可使用when代替if層疊
// when容許檢查實參值的類型 fun eval(e: Expr): Int = when (e) { is Num -> e.value is Sum -> eval(e.left) + eval(e.right) else -> throw IllegalArgumentException("Unknown") }
if 和 when 均可以使用代碼塊做爲分支體,那麼代碼塊中的最後一個表達式就是結果。
fun evalWithLogging(e: Expr): Int = when (e) { is Num -> { println("num: ${e.value}") e.value } is Sum -> { val left = evalWithLogging(e.left) val right = evalWithLogging(e.right) println("sum: $left + $right") left + right } else -> throw IllegalArgumentException("Unknown") }
和java沒有區別,有while循環 和 do-while循環
區間:兩個值之間的間隔,這兩個值一般是數字,一個起始值,一個結束值。使用 .. 運算符表示區間。
kotlin區間是包含的或者閉合的。是包含結束值的。若是不包含結束值,使用 until 函數
val oneToTen = 1..10 // 不包含size for (x in 0 until size) 等價於 for (x in 0..size - 1)
迭代1到100之間的全部數字
fun fizzBuzz(i: Int) = when { i % 15 == 0 -> "FizzBuzz " i % 3 == 0 -> "Fizz " i % 5 == 0 -> "Buzz " else -> "$i " } >>> for (i in 1..100) { print(fizzBuzz(i)) }
迭代帶步長的100到1的區間
// 100 downTo 1 是遞減的數列(步長爲-1) // step 2 把步長的絕對值變成2,可是方向保持不變(步長被設置成了爲-2) for (i in 100 downTo 1 step 2) { print(fizzBuzz(i)) }
初始化map並迭代。..語法能夠建立數字區間,也能夠建立字符區間。
// 初始化map,使用TreeMap讓鍵排序 val binaryMap = TreeMap<Char, String>() // 使用字符區間迭代從A到F之間的字符 for (c in 'A'..'F') { // 字符二進制 val binary = Integer.toBinaryString(c.toInt()) // 簡明語法:map[key] = value 設置值;map[key] 讀取值 // 這行等價於:binaryMap.put(c, binary) binaryMap[c] = binary } // 迭代map,把鍵和值賦給兩個變量 for ((letter, binary) in binaryMap) { println("$letter : $binary") }
能夠在迭代集合時,使用當前下標
val list = arrayListOf("10", "11", "12") // 迭代集合時使用下標 for ((index, element) in list.withIndex()) { println("$index : $element") }
關鍵字 in 能夠迭代區間或者集合,還能夠用來檢查區間或者集合是否包含了某個值。!in 檢查一個值是否不在區間中。
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z' // !in 檢查是否不在區間中 fun isNotDigit(c: Char) = c !in '0'..'9'
in 運算符 和 !in 檢查能夠做爲 when分支
fun recognize(c: Char) = when (c) { // 檢查值是否在0到9的區間以內 in '0'..'9' -> "It's a digit." in 'a'..'z', in 'A'..'Z' -> "It's a letter." else -> "I don't know." }
// 建立set val set = hashSetOf(1, 3, 5) // kotlin的javaClass等價於java的getClass println(set.javaClass) // class java.util.HashSet // 建立list val list = arrayListOf(1, 3, 5) println(list.javaClass) // class java.util.ArrayList // 建立map // to 並非一個特殊的結構,而是一個普通函數 val map = hashMapOf(1 to "one", 3 to "three", 5 to "five") println(map.javaClass) // class java.util.HashMap
kotlin中能夠經過下面方式獲取一個列表中最後一個元素,或者獲得一個數字列表的最大值
val list = listOf("abc", "def", "ker") println(list.last()) // ker val set = setOf(1, 23, 4) println(set.max()) // 23
java集合都有一個默認的toString方法實現。假設不想使用默認的實現,須要本身實現或者使用第三方庫,如:guava 和Apache Commons。
// 本身實現joinToString函數 fun <T> joinToString( collection: Collection<T>, prefix: String, postfix: String, separator: String ): String { val result = StringBuilder(prefix) for ((index, element) in collection.withIndex()) { if (index > 0) result.append(separator).append(" ") result.append(element) } result.append(postfix) return result.toString() } >>> val list = listOf("abc", "def", "ker") >>> println(joinToString(list, "(", ")", ";")) (abc; def; ker)
// 這種方式不能知道每一個參數對應什麼含義 joinToString(list, "(", ")", ";") // kotlin,能夠顯式的標明一些參數名稱 // 若是在調用一個函數時,指明瞭一個參數名稱,爲了不混淆,那它以後全部參數都須要標明名稱 joinToString(list, prefix = "(", postfix = ")", separator = ";")
java中存在一個廣泛的問題,一些類的重載函數不少,如:Thread類(8個構造方法)。
在kotlin中,能夠在聲明函數的時候,指定參數的默認值,就能夠避免建立重載的函數。
fun <T> joinToString( collection: Collection<T>, prefix: String = "", // 有默認的參數 postfix: String = "", separator: String = ", " ): String // 調用的時候,能夠省略只有排在末尾的參數 >>> joinToString(list) >>> joinToString(list, "(", ")") // 若是使用命名參數,能夠省略中間的一些參數 >>> joinToString(list, separator = "; ") // 也能夠以任意順序只給定須要的參數 >>> joinToString(list, separator = "; ", prefix = "[")
在kotlin中,不須要建立靜態工具類,能夠把函數直接放到代碼文件的頂層,不用從屬任何的類。
聲明joinToString做爲頂層函數
// join.kt package strings fun joinToString(...): String { ... } // 當編譯join.kt這個文件時,會生成一些類,JoinKt.java。由於JVM只能執行類中的代碼。 // 且join.kt文件中的全部頂層函數編譯爲JoinKt.java這個類的靜態函數 // 從java中調用這些函數:JoinKt.joinToString(list, "", "", ""); public final class JoinKt { @NotNull public static final String joinToString(@NotNull Collection collection, @NotNull String prefix, @NotNull String postfix, @NotNull String separator) { ... } }
修改文件類名:
要修改包含kotlin頂層函數的生成的類的名稱,須要爲這個文件添加 @file:JvmName 註解,將其放到這個文件的開頭,位於包名的前面
// 註解指定類名 @file:JvmName("StringUtils") // 包的聲明跟在文件註解後 package strings fun <T> joinToString( collection: Collection<T>, prefix: String = "", postfix: String = "", separator: String = ", " ): String { ... } // 編譯後,生成StringUtils.class文件 // 在java代碼中調用這個函數:StringUtils.joinToString(list, "", "", "")
頂層屬性和函數同樣,屬性也能夠放到文件的頂層。
package strings val LINE_SEPARATOR = "\n" // 編譯後,私有的靜態常量。 private static final String LINE_SEPARATOR = "\n"; // 使用const修飾,const val LINE_SEPARATOR = "\n" 編譯後生成public的靜態常量 public static final String LINE_SEPARATOR = "\n";
擴展函數就是把要擴展的類或者接口的名稱,放到即將添加的函數前面。這個類的名稱稱爲接受者類型,用來調用這個擴展函數的那個對象,叫做接受者對象。可是擴展函數不能訪問私有的或者受保護的成員。
package strings // 爲String類添加本身的方法:字符串的最後一個字符 // String:接受者類型 this:接受者對象 fun String.lastChar(): Char = this[this.length - 1] >>> println("abc".lastChar())
kotlin容許用和導入類同樣的語法來導入單個函數。
import strings.lastChar // 使用 * 來導入也能夠: import strings.* // 還可使用關鍵字 as 來修改導入的類或者函數名稱 // import strings.lastChar as last // 調用: val lastChar = "abc".last() val lastChar = "abc".lastChar()
擴展函數就是靜態函數,它把調用對象做爲了它的第一個參數。
// 把接受者對象做爲第一個參數傳進去便可 StringUtils.lastChar("abc")
爲元素的集合類Collection添加一個擴展函數,而後給全部的參數添加一個默認值。
@file:JvmName("StringUtils") package strings import java.lang.StringBuilder // Collection<T>: 接受者類型 // 爲Collection<T> 聲明一個擴展函數 fun <T> Collection<T>.joinToString( prefix: String = "", postfix: String = "", separator: String = ", " ): String { val result = StringBuilder(prefix) // this: 接受者對象 for ((index, element) in this.withIndex()) { if (index > 0) result.append(separator).append(" ") result.append(element) } result.append(postfix) return result.toString() } >>> val list = listOf("abc", "def", "ker") >>> println(list.joinToString(prefix = "{", postfix = "}", separator = "; ")) {abc; def; ker}
kotlin中不能重寫擴展函數,由於kotlin會把它們看成靜態函數對待。
擴展屬性必須定義getter函數,由於沒有支持字段,所以沒有默認getter實現,初始化也不能夠,由於沒有地方存儲初始值。
// 聲明一個擴展屬性 val String.lastChar: Char get() = this[this.length - 1]
也能夠聲明一個可變的擴展屬性
var StringBuilder.lastChar: Char // getter屬性 get() = this.get(this.length - 1) // setter屬性 set(value) { this.setCharAt(length - 1, value) } >>> val sb = StringBuilder("ab?") >>> sb.lastChar = '!' >>> println(sb) // ab! // 若是從java中訪問擴展屬性,顯式的調用它的getter函數 StringUtils.getLastChar(new StringBuilder("abc"));
val list = listOf("abc", "def", "ker") // last函數被定義爲List的擴展函數 println(list.last()) // ker // public fun <T> List<T>.last(): T { ... }
kotlin中,在參數上使用vararg修飾符;java中使用三個點(...)
val list = listOf("abc", "def", "ker") // listOf函數在庫中聲明 public fun <T> listOf(vararg elements: T): List<T>
還有一個區別:當須要傳遞的參數是已經包裝在數組中時,在java中,能夠按原樣傳遞數組;kotlin中要求顯式的解包數組,以便每一個數組元素在函數中能做爲單獨的參數來調用。
val array = arrayOf(11, 12, 13) // 使用展開運算符(*)傳遞數組 val list = listOf("10", *array) >>> println(list) // [10, 11, 12, 13]
val map = mapOf(1 to "one", 2 to "two") // mapOf函數在庫中的聲明 public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> // 通常函數調用: 2.to("two"); 中綴符號調用to函數:2 to "two" // to 不是內置結構,而是一種特殊的函數調用,稱爲中綴調用 // to 函數在庫中的聲明 // 中綴調用能夠與只有一個參數的函數一塊兒使用,使用中綴符號調用函數,須要使用**infix**修飾符標記。 public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
能夠直接用Pair的內容來初始化兩個變量,稱爲解構聲明。解構聲明還可使用map的key和value內容來初始化兩個變量。也適用於循環,如: withIndex
val (number, name) = 1 to "one" val list = listOf("abc", "def", "ker") for ((index, element) in list.withIndex()) { println("$index : $element") }
kotlin能夠在字符串字面值中引用局部變量,只須要在變量名稱前面加上字符$
val x = 12 println("x = $x") // x = 12 // 若是對它轉義,不會把x解析成變量的引用 println("x = \$x") //x = $x // 若是引用複雜的表達式,須要把表達式用花括號括起來 val x= 1 val y = 2 println("x + y = ${x + y}")
// 在java中,指望獲得[12, 345-6, A],可是返回是一個空數組,由於點號(.)表示任何字符的正則表達式 System.out.println(Arrays.toString("12.345-6.A".split("."))); // [] // 在kotlin中,能夠顯示的建立一個正則表達式分割字符串 // 須要轉義表示字面量,而不是通配符 // 使用擴展函數toRegex將字符串轉換爲正則表達式 println("12.345-6.A".split("\\.".toRegex())) // [12, 345-6, A] // kotlin中的split擴展函數的其餘重載支持任意數量的純文本字符串分隔符; // 指定多個分隔符 println("12.345-6.A".split(".", "-")) // [12, 345, 6, A]
使用String的擴展函數來解析文件路徑。
fun parsePath(path: String) { val directory = path.substringBeforeLast("/") val fullName = path.substringAfterLast("/") val fileName = fullName.substringBeforeLast(".") val extension = fullName.substringAfterLast(".") println("Dir: $directory, fileName: $fileName, ext: $extension") //Dir: /User/kerwin/book, fileName: readme, ext: md } >>> parsePath("/User/kerwin/book/readme.md")
使用正則表達式解析文件路徑
fun parsePath(path: String) { // 正則表達式寫在一個 三重引號的字符串中,不須要對任何字符進行轉義,包括反斜線,因此能夠用\.而不是\\.表示點 // (.+) 目錄,/ 最後一個斜線,(.+) 文件名,\. 最後一個點,(.+) 擴展名 val regex = """(.+)/(.+)\.(.+)""".toRegex() val matchResult = regex.matchEntire(path) if (matchResult != null) { val (directory, fileName, extension) = matchResult.destructured println("Dir: $directory, fileName: $fileName, ext: $extension") } }
能夠避免轉義字符,且能夠包含任何字符,包括換行符,用於格式化代碼的縮進。
val string = """| // .|// .|/ \ """ // 能夠去掉縮進 // 先向字符串內容添加前綴,標記邊距的結尾,而後調用trimMargin來刪除每行中的前綴和前面的空格 >>> println(string.trimMargin(".")) | // |// |/ \
帶重複代碼的函數
fun saveUser(user: User) { if (user.name.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, name is empty.") } if (user.address.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, address is empty.") } // save user ... }
提取局部函數來避免重複,在佈局函數中能夠訪問外層函數的參數
fun saveUser(user: User) { fun validate(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Can't save user ${user.id}, $fieldName is empty.") } } validate(user.name, "name") validate(user.address, "address") // save user ... }
提取邏輯到擴展函數
class User(val id: Int, val name: String, val address: String) fun User.validateBeforeSave(value: String, fieldName: String) { if (value.isEmpty()) { throw IllegalArgumentException("Can't save user ${this.id}, $fieldName is empty.") } } fun saveUser(user: User) { user.validateBeforeSave(user.name, "name") user.validateBeforeSave(user.address, "address") // save user ... }
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)