第一章 數據類型
1.基礎數據類型
Kotlin的基本數值類型包括Byte、Short、Int、Long、Float、Double等。不同於Java的是,字符不屬於數值類型。
每個數據類型都有如下轉換函數
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
字面值常量
主要是以下幾種字面值常量:
--十進制數值: 123
--長整型要加大寫 L : 123L
--16進制:0x0f
--二進制:0b00001011
注意不支持8進制
Kotlin 也支持傳統的浮點數表示:
-- 默認雙精度浮點數(Double) : 123.5 , 123.5e10
-- 單精度浮點數(Float)要添加 f 或 F :123.5f
數值常量中可以添加下劃線分割(1.1版本新特性)
您可以使用下劃線增加數值常量的可讀性:
-- val oneMillion = 1_000_000
-- val creditCardNumber = 1234_5678_9012_3456L
-- val socialSecurityNumber = 999_99_9999L
-- val hexBytes = 0xFF_EC_DE_5E
-- val bytes = 0b11010010_01101001_10010100_10010010
值相等與物理值相比較
val a: Int = 10000
print (a === a ) // 打印 'true' ---注意區別是在這個三元等號
val boxedA: Int? =a
val anotherBoxedA: Int? = a
print (boxedA === anotherBoxedA ) // 注意這裏打印的是 'false'
另一方面,它們是值相等的:
val a: Int = 10000
print(a == a) // 打印 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
print(boxedA == anotherBoxedA) // 打印 'true'
2.變量和常量
1).變量
//定義一個字符變量,自動推導爲字符串
var name ="張三丰" //也可以這樣寫 var name:String = "張三丰"
//或者
var addr:String="深圳"
//或者
var addr:String?="深圳"
2).常量
//定義一個常量
val company ="小牛在線"
//定義一個可以爲空的量
val friend:String="張三"
3.字符串
1).字會串拼接
var name = "XiaoMin"
var age = 23
var xiaomin = "小明的名字是$name,年紀是$age"
println(xiaomin)
2).複雜字符拼接
//定義一個類
class Animal{
var name:String="Animal"
var age:Int = 10
}
//主函數
fun main(args: Array<String>) {
var animal = Animal()
println("animal name is ${animal.name}") //拼接字符
}
3).多行字符串
val text = """
Tell me and I forget
Teach me and I remember
Involve me and I learn
(Benjamin Franklin)
"""
//如果要去掉空字符,可以這樣. 類似java的trim()
var text =""" Tell me and I forget """.trim-margin()
4.Any數據類型
Any是Kotlin所有數據的基類,類似於Java的Object. 但是它沒有hashCode,toString等方法, 這是和java的區別.
var pig:Any = Animal("pig")
var dog:Any = Animal("dog")
class Animal(name:String) {}
5.集合數據類型
1). List集合的用法
var list = listOf<Pig>(
Pig("大豬", 10, "F"),
Pig("黑豬", 20, "M"),
Pig("白豬", 30, "F"),
Pig("小豬", 40, "M")
)
var list2 = listOf<Int>(
100, 1, 2, 3, 4, 5
)
//直接設用java的接口對集合排序
Collections.sort(list2)
//調用Kotlin接口排序
list.sortedBy({
it.age
})
//輸出集合中的數據
for ((i, item) in list2.withIndex()) {
println("$i is $item")
}
for ((i, item) in list.withIndex()) {
println("${item.name} is ${item.age}")
}
2). Set集合的用法
//顯示定義一個無序集合
var addr:Set<String> = setOf("深圳","上海")
//輸出集合數據內容
for(item in addr.iterator()){
println(item)
}
//定義有序的Set
var friends:SortedSet<Int> = sortedSetOf(10,5,30,20,40)
//輸出集合數據內容
for(item in friends.iterator()){
println(item)
}
3).Map集合的用法
//顯示定義一個集合
var addr:Map<Int,String> = mapOf<Int,String>(1 to "深圳", 2 to "上海")
//或者
var friends = mapOf<Int,String>(1 to "小平",2 to "毛澤東")
//輸出集合內容
for( (K,V) in addr.entries){
println("集合數據Key=$K,Value=$V")
}
for( K in addr.keys ){
println("集合數據Key=$K,Value=${addr.get(K)}")
}
//定義有序的map
var sortMap:SortedMap<Int,String> = sortedMapOf(2 to "深圳", 3 to "上海",1 to "南京")
for( K in sortMap.keys ){
println("有序Map Key=$K,Value=${addr.get(K)}")
}
6.數組
數組的用法簡單好用,如下例子
fun main(args:Array<String>){
var intArray = intArrayOf(1,2,3,4,5)
var longArray = longArrayOf(1L,2L,3L,4L,5L)
var floatArray = floatArrayOf(10f,20f,30f,40f,50f)
var doubleArray = doubleArrayOf(10.1,10.2,20.3,30.3,40.4e10)
var byteArray = byteArrayOf(1,2,3,4)
var shortArray = shortArrayOf(1,23,3,4)
var animals:Array<Animal> = arrayOf(Animal("狗"), Animal("豬"))
}
class Animal(name:String){
var name:String
init {
this.name = name
}
}
第二章 操作符
1.判空操作符
1.判空操作符 ?
var name:String?="小平同志" //表示該變量可以爲空
2.如果爲空 ?:
var name:String?="小平同志" //表示該變量可以爲空
var rename = name?:"習近平同志" //如果name不爲null就把值給rename,否則rename就重新賦值
3.如果不爲空才做一件事 ?.
var addr:String?="深圳"
//addr不爲空才執行let中的任務
addr?.let {
println("to do something")
}
4.函數返值可以爲空
fun request():Response?{
return null
}
class Response
2.等號操作符
相等,在 kotlin 中有兩種相等:
1).===表示: 參照相等(指向相同的對象):內存單元相同
2).== 表示: 結構相等
1).參照相等
參照相等是通過 === 操作符判斷的(不等是!== ) a===b 只有 a b 指向同一個對象是判別才成立。
另外,你可以使用內聯函數 identityEquals() 判斷參照相等:
a.identityEquals(b)
a identityEquals b
2).結構相等
結構相等是通過 == 判斷的。像 a == b 將會翻譯成:
a?.equals(b) ?: b === null
如果 a 不是 null 則調用 equals(Any?) 函數,否則檢查 b 是否參照等於 null
注意完全沒有必要爲優化你的代碼而將 a == null 寫成 a === null 編譯器會自動幫你做的。
注意: 上面兩種等號,字面意思有所不同,但實際效果沒有區別.
var x1 ="a"
var x2 ="a"
println(x1 == x2)
println(x1 === x2)
println(x1.equals(x2))
//結果都是輸出 true
3. in 和downTo
for (i in 20 downTo 15)
{
println(i)
}
for (i in 1..10) {
println(i)
}
第三章 控制流
1.IF表達式
在Kotlin中, 沒有三元表達式,傳統用法:
var max = a
if (a < b)
max = b
// With else
var max: Int
if (a > b)
max = a
else
max = b
IF 作爲表達式的用法:
var a:Int = 1
var b:Int = 2
val max:Int = if (a > b) a else b
val min = if (a > b) a else b
IF作爲返回值的用法:
fun asRetunExpression1(a:Int,b:Int):Int = if (a > b) a else b
fun asRetunExpression2(a:Int,b:Int):Int {
return if (a > b) a else b
}
fun asRetunExpression3(a:Int,b:Int):Int {
if (a > b) return a else return b
}
2.When表達式
替代Switch的用法
var x = 1
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
常規的When用法
var x ="2"
var i = 2
when (i) {
parseInt(x) -> print("s encodes x")
else -> print("s does not encode x")
}
我們可以在判斷分支條件的地方使用任何表達式,而不僅僅是常量(和switch不同):
var x = 3
var validNumbers = 100..200
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
in , !in在When中的使用
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
有參數When,可以用 is來檢測類型
val hasPrefix = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
無參數When的使用
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
查看 grammar for when{: .keyword }.
3.For循環
For循環主要有以下用法
//輸出一個範圍內的值
fun forExample1() {
for (i in 1..10) {
println(i)
}
}
//從20輸出到位15
fun forExample2() {
for (i in 20 downTo 15) {
println(i)
}
}
//輸出集全元素
fun forExample3() {
val arrayInts = arrayListOf("one", "two", "three")
for ((i, item) in arrayInts.withIndex()) {
println("index = $i, item value is = $item")
}
}
//in關鍵字在表達式中的用法
fun forExample4() {
var char = 'A'
var result = char in 'A'..'Z' || char in 'a'..'z'
println(result)
}
4.While循環
while{: .keyword } 和 do{: .keyword }..while{: .keyword } 的使用方法和其他語言一致
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!
第四章 類和繼承
1.類
類聲明Kotlin使用關鍵字class
class Invoice {}
或者
clas Invoice
這個類聲明被花括號包圍,包括類名、類頭(指定其類型參數,主構造函數等)和這個類的主幹。類頭和主幹都是可選的; 如果這個類沒有主幹,花括號可以被省略。
class Empty
2.構造
在Kotlin中的類可以有主構造函數和一個或多個二級構造函數。主構造函數是類頭的一部分:它跟在這個類名後面(和可選的類型參數)
class Person constructor(firstName: String) {
}
如果這個主構造函數沒有任何註解或者可見的修飾符,這個constructor{: .keyword }關鍵字可以被省略
class Person(firstName: String) {
}
這個主構造函數不能包含任何的代碼。初始化的代碼可以被放置在initializer blocks(初始的模塊),以init爲前綴作爲關鍵字{:.keyword}
class Customer(name: String) {
init {
logger.info("Customer initialized with value ${name}")
}
}
請注意,主構造的參數可以在初始化模塊中使用。它們也可以在類體內聲明初始化的屬性:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
事實上,聲明屬性和初始化主構造函數,Kotlin有簡潔的語法:
class Person(val firstName: String, val lastName: String, var age: Int) {
// ...
}
與普通屬性一樣,主構造函數中聲明的屬性可以是可變的或者是隻讀的
If the constructor has annotations or visibility modifiers, the constructor{: .keyword } keyword is required, and the modifiers go before it: 如果構造函數有註解或可見性修飾符,這個constructor{: .keyword }需要被關鍵字修飾。
class Customer public inject constructor(name: String) { ... }
3.擴展構造函數
類也可以擁有被稱爲”二級構造函數”(爲了實現Kotlin向Java一樣擁有多個構造函數),通常被加上前綴”constructor”
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
如果類有一個主構造函數,每個二級構造函數需要委託給主構造函數,直接或間接地通過另一個二級函數。 委託到另一個使用同一個類的構造函數用this關鍵字
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一個非抽象類沒有聲明任何構造函數(原發性或繼發性),這將有一個生成的主構造函數不帶參數。構造函數的可見性是public。
如果你不希望你的類有一個公共構造函數,你需要聲明與非缺省可見一個空的主構造函數:
class DontCreateMe private constructor () {
}
注意在JVM上,如果所有的主構造函數的參數有默認值,編譯器會產生一個額外的參數的構造函數,將使用默認值。
class Customer(val customerName: String = "")
4.創建類的實例
要創建一個類的實例,我們調用構造函數,就好像它是普通的函數:
val invoice = Invoice()
val customer = Customer("Joe Smith")
注意Kotlin不能有“new”關鍵字
5.類成員
類可以包括
- 構造和初始化模塊
- 函數
- 屬性
- 匿名和內部類
- 對象聲明
- 繼承
- 伴隨對象
在Kotlin所有的類中都有一個共同的父類Any,這是一個默認的父類且沒有父類型聲明:
class Example // Implicitly inherits from Any
Any不屬於java.lang.Object;特別是,它並沒有任何其他任何成員,甚至連equals(),hashCode()和toString()都沒有。
要聲明一個明確的父類,我們把類型放到類頭冒號之後:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
如上所見,父類可以(並且必須)在聲明繼承的地方,用原始構造函數初始化。
如果類沒有主構造,那麼每個次級構造函數初始化基本類型 使用super關鍵字,或委託給另一個構造函數做到這一點。 注意,在這種情況下,不同的二級構造函數可以調用基類型的不同的構造:
class MyView : View {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
}
}
父類上的open標註可以理解爲Java中final的反面,它允許其他他類 從這個類中繼承。
默認情況下,在Kotlin所有的類都是final,.
對應於 Effective Java 書中的17條:設計並顯示標註繼承,否則就禁止它。
伴隨對象實現單例,靜態變量的效果
class Student(name: String) {
var name: String? = null
init {
this.name = name
}
companion object {
@Volatile private var student: Student?= null
fun getSingleton():Student? {
if (this.student == null) {
synchronized(this) {
this.student = Student("文忠湖")
}
}
return this.student
}
}
fun walk(){
println("walk")
}
}
fun main(args:Array<String>){
var student:Student? = Student.getSingleton()
student?.walk()
}
6.覆蓋成員
我們之前提到過,Kotlin力求清晰顯式。不像Java中,Kotlin需要明確的 標註覆蓋的成員(我們稱之爲open)和重寫的函數。(繼承父類並覆蓋父類函數時,Kotlin要求父類必須有open標註,被覆蓋的函數必須有open標註,並且子類的函數必須加override標註。):
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
Derived.v()函數上必須加上override標註。如果沒寫,編譯器將會報錯。 如果父類的這個函數沒有標註open,則子類中不允許定義同名函數,不論加不加override。 在一個final類中(即沒有聲明open的類),函數上也不允許加open標註。
成員標記爲override 的本身是開放的,也就是說,它可以在子類中重寫。如果你想禁止重寫的,使用final 關鍵字:
open class AnotherDerived() : Base() {
final override fun v() {}
}
在Kotlin中,實現繼承的調用通過以下規則:
如果一個類繼承父類成員的多種實現方法,可以直接在子類中引用, 它必須重寫這個成員,並提供其自己的實現(當然也可以使用父類的)。
爲了表示從中繼承的實現而採取的父類型,我們使用super 在尖括號,如規範的父名super:
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are 'open' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}
類C同時繼承A和B是可以的,而且我們在調用a()和b()函數時沒有任何問題,因爲他們在C的基類中只有一個實現。 但是f()函數則在A,B中都有實現,所以我們必須在C中覆蓋f(),並且提供我們的實現以消除歧義。
抽象類
類和其中的某些實現可以聲明爲abstract{:.keyword}。 抽象成員在本類中可以不用實現。。 因此,當一些子類繼承一個抽象的成員,它並不算是一個實現:
abstract class A {
abstract fun f()
}
interface B {
open fun f() { print("B") }
}
class C() : A(), B {
// We are not required to override f()
}
需要注意的是,我們並不需要標註一個抽象類或者函數爲open
我們可以重寫一個open非抽象成員使之爲抽象的。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
第五章 屬性和字段
1.聲明屬性
Kotlin的屬性. 這些聲明是可變的,用關鍵字var 或者使用只讀關鍵字val .
public class Address {
public var name: String = ...
public var street: String = ...
public var city: String = ...
public var state: String? = ...
public var zip: String = ...
}
要使用一個屬性,只需要使用名稱引用即可,就相當於Java中的公共字段:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
2.延遲屬性
Kotlin聲明一個屬性一定要初始化, 當你不想初始化的時候就可以用lazy關鍵字來處理了
val lazyValue: String by lazy {
println("computed!") //第一次初始化,會輸出這一行內容
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
3.getters和setters
聲明一個屬性的完整語法
var <propertyName>: <PropertyType> [= <property_initializer>]
<getter>
<setter>
上面的定義中,初始器(initializer)、getter和setter都是可選的。 屬性類型(PropertyType)如果可以從初始器或者父類中推導出來,也可以省略。
例如:
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
注意公有的API(即public和protected)的屬性,類型是不做推導的。 這麼設計是爲了防止改變初始化器時不小心改變了公有API。比如:
public val example = 1 // error: a public property must have a type specified explicitly
一個只讀屬性的語法和一個可變的語法有兩方面的不同:
- 1·只讀屬性的用val開始代替var
- 2·只讀屬性不許setter
val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
我們可以編寫自定義的訪問器,非常像普通函數,對內部屬性聲明。這裏有一個定義的getter的例子:
val isEmpty: Boolean
get() = this.size == 0
一個定義setter的例子:
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
按照慣例,setter參數的名稱是“value”,但是如果你喜歡你可以選擇一個不同的名稱。
如果你需要改變一個訪問器或註釋的可見性,但是不需要改變默認的實現, 您可以定義訪問器而不定義它的實例:
var setterVisibility: String = "abc" // Initializer required, not a nullable type
private set // the setter is private and has the default implementation
var setterWithAnnotation: Any?
@Inject set // annotate the setter with Inject
第六章 接口
Kotlin 的接口很像 java 8。它們都可以包含抽象方法,以及方法的實現。和抽象類不同的是,接口不能保存狀態。可以有屬性但必須是抽象的。
接口是通過關鍵字 interface 來定義的:
interface MyInterface {
fun bar()
fun foo() {
//函數體是可選的-和java8類似
}
}
1.接口的實現
一個類或對象可以實現一個或多個接口
class Child : MyInterface {
fun bar () {
//函數體
}
}
interface MyInterface {
val property: Int //抽象屬性
fun foo() {
print(property)
}
}
class Child : MyInterface {
override val property: Int = 29
}
2.抽象類
用關鍵字abstract 定義抽象類 ,抽象方法也需要用abstract .
interface Biology{
fun walk()
fun eat()
}
abstract class Animal(name: String, age: Int):Biology {
open fun sleep(){
println("sleep()")
}
abstract fun laugh()
}
3.繼承
注意構造方法的傳參,Kotlin也只支持單繼承.
class Pig(name:String,age:Int,sex:String): Animal(name,age,sex){
override fun laugh() {
println(" $name 都笑了.")
}
override fun walk() {
println("$sex $name $age years old : walk. ")
}
override fun eat() {
println("$sex $name $age years old : eat.")
}
override fun sleep() {
println("$sex $name $age years old : sleep.")
}
}
4.解決衝突
當我們在父類中聲明瞭許多類型,有可能出現一個方法的多種實現。比如:
interdace A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
A B 接口都有聲明瞭 foo() bar() 函數。它們都實現了 foo() 方法,但只有 B 實現了 bar() ,bar() 在 A 中並沒有聲明它是抽象的,這是因爲在接口中如果函數沒有函數體,那麼默認是抽像的。
現在,如果我們從 A 中派生一個 C 實體類,顯然我們需要重寫 bar() ,並實現它。而我們從 A 和 B 派生一個 D ,我們不用重寫 bar() 方法,因爲我們的一個繼承中有一個已經實現了它。
但我們繼承了倆個 foo() 的實現,因此編譯器不知道應該選哪個,並強制我們重寫 foo() 並且明確指出我們想怎麼實現。
第七章 函數與屬性擴展
Kotlin 與 C# Gosu 類似,提供了不用從父類繼承,或者使用像裝飾模式這樣的設計模式來給某個類進行擴展。這是通過叫擴展的特殊聲明來達到的。現在, Kotlin 支持擴展函數和屬性。
1.擴展函數
聲明一個擴展函數,我們需要添加一個接收者類型的的前綴。
下面是給MutableList添加一個 swap 函數的例子:
fun MutableList<Int>.swap(x: Int, y: Int) {
val temp = this[x] // this 對應 list
this[x] = this[y]
this[y] = tmp
}
在擴展函數中的 this 關鍵字對應接收者對象。現在我們可以在任何 MutableList 中使用這個函數了:
fun <T> MutableList<T>.swap(x: Int, y: Int) {
val tmp = this[x] // 'this' corresponds to the list
this[x] = this[y]
this[y] = tmp
}
2.可空的接受者
值得注意的是擴展可以定義一個可空的接受者。這樣的擴展可以算作對象的變量,即使它是空的,你可以在函數體內檢查 this == null 。這樣你就可以在 Kotlin 中不進行空檢查就可以調用 toString() 方法:這樣的檢查是在擴展函數中做的。
fun Any?.toString(): String {
if (this == null) return "null"
return toString()
}
3.擴展屬性
和函數類似, Kotlin 也支持屬性擴展:
val <T> List<T>.lastIndex: Int
get() = size-1
注意,擴展並不是真正給類添加了成員,沒有比給擴展添加備用字段更有效的辦法了/這就是爲什麼初始化函數是不允許擴展屬性。它們只能通過明確提供 getter setter 來作用。
4.伴隨對象擴展
如果一個對象定義了伴隨對象,你也可以給伴隨對象添加擴展函數或擴展屬性:
class MyClass {
companion object {}
}
fun Myclass.Companion.foo() {
}
和普通伴隨對象的成員一樣,它們可以只用類的名字就調用:
MyClass.foo()
5.擴展的範圍
大多數時候我們在 top level 定義擴展,就在包下面直接定義:
package foo.bar
fun Baz.goo() { ... }
在聲明的包外面使用這樣的擴展,我們需要在 import 時導入:
package com.example,usage
import foo.bar.goo//導入所有名字叫 "goo" 的擴展
import foo.bar.*
fun usage(baz: Baz) {
baz.goo()
}
6.可變參數Varargs
fun request(vararg params:String){
for( (i,item) in params.withIndex() ){
println("$i,$item")
}
}
fun main(args:Array<String>){
request("fruit","pig","plant")
}
第八章 數據類
我們經常創建一些只是處理數據的類。在這些類裏的功能經常是衍生自他們所持有的數據。在Kotlin中,這樣的類可以被稱爲data:
data class User(val name: String, val age: Int)
這被叫做一個 數據類。編譯器自動從在主構造函數定義的全部特性中得到以下成員:
equals()/hashCode(),
toString() 格式是 "User(name=John, age=42)",
componentN() functions 對應按聲明順序出現的所有屬性,
copy() 方法 .
如果有某個函數被明確地定義在類裏或者被繼承,編譯器就不會生成這個函數。
NOTE如果一個構造參數沒有val或者var在前面,它將不會被包括進這些成員裏;也不會在類裏聲明成屬性或者繼承自父類
在JVM中,如果生成的類需要含有一個無參的構造函數,則所有的屬性必須有默認值。
data class User(val name: String = "", val age: Int = 0)
1.複製
在很多情況下,我們我們需要對一些屬性做修改而其他的不變。這就是copy()這個方法的來源。對於上文的User類,應該是這麼實現這個方法的
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
也可以這麼寫
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
2.數據類和多重聲明
成員方法用於使數據類可以多聲明:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
3.標準數據類
在標準庫提供了Pair和Triple。在很多情況下,即使命名數據類是一個更好的設計選擇,因爲這能讓代碼可讀性更強。
第九章 內部類
1.靜態內部類
不可以訪外部類的屬性
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val nested = Outer.Nested()
nested.foo()
2.非靜態內部類
加上Inner關鍵字,就是非靜態內部類. 內部類會帶有一個來自外部類的對象的引用,且可以訪問外部類的屬性方法.
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
val inner= Outer.Inner()
inner.foo()
第十章 安全構建
請參考官網:
第十一章 其它
1.異常
1). try作爲表達式
fun tryException(){
var result = try{
Integer.parseInt("2"); //如果轉換成功,結果會賦值給result
}catch (a: Exception){
null //如果轉換失敗,null會賦值給result
}finally{
println("not as a result") // 這裏不會作爲結果賦值給result
}
println(result)
}
2).自定義異常
類似於java一樣的寫法, 實際上它就是調用的java的異常基類
class ExceptionExtend(message:String):Exception(message){
}
我們可以查看Kotlin源碼,他作了一個映射, Kotlin映射java裏面的類
@SinceKotlin("1.1") public typealias Error = java.lang.Error
@SinceKotlin("1.1") public typealias Exception = java.lang.Exception
2.反射
直接看例子, 網上所有教程都只對 ::簡單說明了一下,在這裏給出例子更容易懂.
fun main(args:Array<String>){
var tmp = Teacher()
println(tmp::class)
println(tmp::class.simpleName)
println(tmp::class.qualifiedName)
var members = tmp::class.members
for( (i,item) in members.withIndex()){
if(item.name == "walk")
item.call(tmp) //類似java的invoke方法.
}
}
class Student{
var name:String?="student"
var age:Int=1
fun walk(){
println("walk")
}
}
3.對象解構
下面代碼不能正常運行,暫時找不到足夠資料解,先當作瞭解.
var teacher = Teacher("習近平",50)
var (name:String?,age:Int) = teacher
4.this表達式
這是官網給出的例子:
class A { // implicit label @A
inner class B { // implicit label @B
fun Int.foo() { // implicit label @foo
val a = this@A // A's this
val b = this@B // B's this
val c = this // foo()'s receiver, an Int
val c1 = this@foo // foo()'s receiver, an Int
val funLit = lambda@ fun String.() {
val d = this // funLit's receiver
}
val funLit2 = { s: String ->
// foo()'s receiver, since enclosing lambda expression
// doesn't have any receiver
val d1 = this
}
}
}
}
5.類的代理
使用by關鍵代理一個類, 它是替換繼承的一個不錯方案.
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 一定要是接口纔可以被代理, 這裏的Base是接口.
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}
7.屬性代理
有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現它們, 但是如果能夠爲大
家把他們只實現一次並放入一個庫會更好。例如包括
- 延遲屬性(lazy properties): 其值只在首次訪問時計算,
- 可觀察屬性(observable properties): 監聽器會收到有關此屬性變更的通知,
- 把多個屬性儲存在一個映射(map)中,而不是每個存在單獨的字段中。
爲了涵蓋這些(以及其他)情況,Kotlin 支持 委託屬性:
class Example {
var p: String by Delegate()
}
屬性代理不必實現任何的接口,但是需要提供一個 getValue() 函數(和 setValue()
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
當我們從委託到一個 Delegate 實例的 p 讀取時,將調用 Delegate 中的 getValue() 函
數, 所以它第一個參數是讀出 p 的對象、第二個參數保存了對 p 自身的描述.
可觀察屬性 Observable
Delegates.observable() 它有三個 參數:被賦值的屬性、舊值和新值:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<default>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
//這個例子輸出:
<default> -> first
first -> second
如果你想能夠截獲一個賦值並“否決”它,就使用 vetoable() 取代 observable() 。 在屬性被
賦新值生效之前會調用傳遞給 vetoable 的處理程序。
import kotlin.properties.Delegates
class User {
var name: String by Delegates.vetoable(""){
prop, old, new ->
if(new =="not allow value")
false
else
true
}
}
把屬性儲存在映射中
一個常見的用例是在一個映射(map)裏存儲屬性的值。 這經常出現在像解析 JSON 或者做
其他“動態”事情的應用中。 在這種情況下,你可以使用映射實例自身作爲委託來實現委託屬
性。
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
在這個例子中,構造函數接受一個映射參數:
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
委託屬性會從這個映射中取值(通過字符串鍵——屬性的名稱):
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
//這也適用於 var 屬性,如果把只讀的 Map 換成 MutableMap 的話:
class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
8.註解
annotation class Document
9.別名
1).當一個類型特別長的時候,就非常有用.
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
2).你也可以爲函數起別名
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
3).也可以爲內部類起別名
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner