Kotlin 筆記(未完)

第一次知道kotlin這個(gè)語(yǔ)言是在JakeWharton的這個(gè)dex-method-list 項(xiàng)目里,本來(lái)這個(gè)項(xiàng)目用的是java后來(lái)大神又用Kotlin實(shí)現(xiàn)了一下娜亿,前兩天1.0正式版發(fā)布了拳球,被各種新聞刷了一下后有必要學(xué)習(xí)一下横浑。

花了半天看完了《Kotlin for Android》這份文檔丘薛,之前很多人說(shuō)像Swift,看完這份文檔后才發(fā)現(xiàn)它更像C#渐尿,比如帶問(wèn)號(hào)的可null類型,委托矾瑰,lambda和的Linq類似砖茸,還有 explicit,implicit cast (協(xié)變逆變),sealed(密封類),里面還提供了和Linq To Sql 很像的Anko框架,以及getter殴穴,setter渔彰。

一. Android下的Kotlin

按照慣例我們做一個(gè)"Helloworld"項(xiàng)目來(lái)嘗嘗鮮

環(huán)境配置

使用Kotlin開發(fā)Android應(yīng)用需要安裝Android Studio 和其配套的插件
需要安裝Kotlin插件嵌屎,是使Android Studio支持Kotlin的基礎(chǔ)插件,在《Kotlin for Android》一書中說(shuō)還需要 Kotlin Extensions For Android 插件恍涂,這個(gè)插件在最近的版本中已經(jīng)obsolote宝惰,這個(gè)插件已經(jīng)被集成到Kotlin插件里,所以不需要安裝這個(gè)插件了再沧。

enter description here

1. 使用AndroidStudio創(chuàng)建一個(gè)新項(xiàng)目

我是用的Android Studio是最新的2.0 Beta5尼夺,Gradle是2.10

enter description here

2. 將MainActivity類轉(zhuǎn)換成Kotlin代碼

這是一個(gè)很給力的特性,可以把Java代碼無(wú)縫的轉(zhuǎn)換成Kotlin代碼炒瘸。這種轉(zhuǎn)換并不是最優(yōu)化的淤堵,但是在學(xué)習(xí)Kotlin的時(shí)候這個(gè)方法很有用,可以對(duì)比之前java代碼和Kotlin代碼語(yǔ)法的轉(zhuǎn)換顷扩。
先打開MainActivity類拐邪,然后有三種方式進(jìn)行轉(zhuǎn)換:

  1. 選擇code->Convert File To Kotlin File
  2. 選擇Help->Find Action或者使用快捷鍵 在彈出的對(duì)話框里輸入convert 就會(huì)有提示,如下圖
  3. 直接使用快捷鍵shift+alt+command可以直接觸發(fā)轉(zhuǎn)換工具
enter description here

3. 配置Gradle

代碼轉(zhuǎn)換完成后會(huì)提示配置Kotlin隘截,可以手動(dòng)配置也可以使用Android Studio自帶的配置工具進(jìn)行配置扎阶。
暫不介紹手動(dòng)配置,自動(dòng)配置會(huì)在第一次代碼轉(zhuǎn)換完成后有個(gè)configure提示(如下圖)婶芭,選擇ok即可自動(dòng)更新Gradle配置文件东臀,非常方便

enter description here

配置完成后Gradle配置文件里會(huì)自動(dòng)加上kotlin gradle的插件配置


enter description here

之后選擇sync項(xiàng)目,將gradle插件下載下來(lái)即可運(yùn)行項(xiàng)目了犀农。


enter description here

4. 運(yùn)行項(xiàng)目

Kotlin里有個(gè)對(duì)Android開發(fā)來(lái)說(shuō)很好用的特性就是它剔除了findViewById()方法的調(diào)用惰赋。

配置gralde

使用這個(gè)特性需要在app的build.gradle文件配置里增加下面這個(gè)plugin引用。

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
//增加這句
apply plugin: 'kotlin-android-extensions'

修改代碼

activity_main.xml修改如下

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="dodola.kotlinandroid1.MainActivity">

    <TextView
        android:id="@+id/helloText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</RelativeLayout>

修改MainActivity.kt代碼如下:

package dodola.kotlinandroid1

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        helloText.text="hello dodola"
    }
}

我們需要import進(jìn)一個(gè)特殊的包呵哨,import kotlinx.android.synthetic.main.activity_main.* 這個(gè)包有個(gè)命名規(guī)則赁濒,后面再介紹。
helloText.text="hello dodola"代碼中可以看出不需要調(diào)用findViewById孟害,也沒有調(diào)用setText方法流部,而是直接使用text來(lái)進(jìn)行賦值,這個(gè)使用的是Kotlin的一個(gè)叫做Getter Setter技術(shù)纹坐,這種機(jī)制類似C#里的Accessors(訪問(wèn)器)枝冀,后面再詳細(xì)介紹,會(huì)發(fā)現(xiàn)很多特性都可以在C#里找到相應(yīng)的技術(shù)耘子,我之前是做.Net開發(fā)的果漾,所以對(duì)C#相對(duì)敏感,后面會(huì)拿C#和Swift和Kotlin做對(duì)比谷誓,這樣也有助于理解Kotlin里的一些技術(shù)绒障。

二. Kotlin 介紹

下面介紹一下Kotlin語(yǔ)言的一些細(xì)節(jié)。內(nèi)容是從官方文檔和其他文章里總結(jié)得來(lái)的捍歪,目標(biāo)是可以快速的學(xué)習(xí)Kotlin語(yǔ)言户辱。

1. 基礎(chǔ)

1.1 基本類型

Kotlin中沒有像Java中的原始基本類型鸵钝,基本類型都被當(dāng)做對(duì)象形式存在。

數(shù)值類型

Kotlin提供的數(shù)值類型如下:

Type Bitwidth
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

需注意Char在Kotlin中不屬于數(shù)值類型

字面值常量

整型:123
  長(zhǎng)整型:123L //需要增加L后綴
16進(jìn)制:0x0f
二進(jìn)制:0b00001011
Double:123.5庐镐,123.5e10//默認(rèn)是Double類型
Float:123.5f //Float類型需要增加F或f后綴

注:不支持8進(jìn)制字面值常量

val i = 12 // An Int
val iHex = 0x0f // 一個(gè)十六進(jìn)制的Int類型
val l = 3L // A Long
val d = 3.5 // A Double
val f = 3.5F // A Float

顯式轉(zhuǎn)換

數(shù)值類型不會(huì)自動(dòng)轉(zhuǎn)型恩商,必須要顯示的進(jìn)行類型轉(zhuǎn)換,例子

//Byte -> Int
val b:Byte =1
val i:Int=b.toInt()

比如下面這種情況:

fun pow(a:Int):Double{
    return Math.pow(a.toDouble(),2);//此處會(huì)出現(xiàn)Kotlin: The integer literal does not conform to the expected type kotlin.Double 的編譯錯(cuò)誤
}

上面例子pow(double a, double b) 傳入的是兩個(gè)Double類型必逆,但是直接寫入Int類型并不會(huì)進(jìn)行隱式轉(zhuǎn)換怠堪。需要做一次顯示轉(zhuǎn)換

fun pow(a:Int):Double{
    return Math.pow(a.toDouble(),2.toDouble());
}

每個(gè)數(shù)值類型都支持下面轉(zhuǎn)換:

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

隱式轉(zhuǎn)換,基于運(yùn)算符重載技術(shù)名眉,例子:

val l=1.toLong() + 1 //Long+Int=>Long

字符類型Char

字符類型使用Char表示粟矿,不能當(dāng)成數(shù)值使用。

和Java一樣损拢,字符類型值是用單引號(hào)包起來(lái)的1,'\n'陌粹,可以顯示轉(zhuǎn)換成Int類型

val c:Char='c'
val i: Int = c.toInt()

布爾值

val booltest=true

字符串

使用String聲明,Kotlin中有兩種樣式的String

一種是和Java類似的聲明:

val s="Hello world"

一種是多行形式的表示

val text = """
    for (c in "foo")
        print(c)
"""

運(yùn)行結(jié)果如下圖:


enter description here

可以像數(shù)組那樣訪問(wèn)福压,并且可以被迭代:

val s = "Example"
val c = s[2] // 這是一個(gè)字符'a'
// 迭代String
val s = "Example"
for(c in s){
    print(c)
}

模板

字符串可以包含模板表達(dá)式掏秩,模板表達(dá)式由一個(gè) $ 開始并包含另一個(gè)簡(jiǎn)單的名稱:

val i = 10
val s = "i = $i" 
//結(jié)果為 i = 10

或者是一個(gè)帶大括號(hào)的表達(dá)式:+

val s = "abc"
val str = "$s.length is ${s.length}"
//結(jié)果為 abc.length is 3

1.2 數(shù)組

創(chuàng)建數(shù)組

指定長(zhǎng)度

    val sizeArray=arrayOfNulls<Int>(5)//sizeArray: Integer[5]{null}
    val sizeArray1=IntArray(5)//sizeArray1: {0,0,0,0,0}
    //同樣的類型還有:BooleanArray,ByteArray,IntArray,CharArray,DoubleArray,FloatArray,LongArray,ShortArray,

使用值創(chuàng)建

 //使用裝箱操作
    val sizeArray2=arrayOf(1,2,3,4,5)
    /**
    sizeArray2: Integer[]
    0 = {Integer@447} "1"
    1 = {Integer@448} "2"
    2 = {Integer@449} "3"
    3 = {Integer@450} "4"
    4 = {Integer@451} "5"
    **/
    //下面方法創(chuàng)建的數(shù)組是未裝箱的
    val sizeArray3=intArrayOf(1,2,3,4,5)//sizeArray3:int[5] {1,2,3,4,5}
    //同類的方法還有
    //booeanArrayOf,byteArrayOf,doubleArrayOf,floatArrayOf,intArrayOf,longArrayOf,shortArrayOf

空數(shù)組

    val emptyArray=emptyArray<Int>()//emptyArray: Integer[0]

訪問(wèn)數(shù)組

val arr = arrayOf(1, 2, 3)
println(asc[1])         //  1
println(asc.get(1))     //  1

遍歷數(shù)組

for(i in arr){
    println(i)
}
//1 2 3
for(j in asc.indices){
    println(j)
}
//0 1 2 

1.3 基本語(yǔ)法

定義包名

package my.demo //在源文件的最開頭
import java.util. 

包名不必和文件夾路徑一致

定義類

class MainActivity{
//包含一個(gè)默認(rèn)的構(gòu)造器
}

構(gòu)造方法

class Datas{
    
}

定義方法

//第一種形態(tài)
fun pow(a:Int):Double{
    return Math.pow(a.toDouble(),2.toDouble());
}
//第二種形態(tài),一個(gè)表達(dá)式函數(shù)體和一個(gè)可推斷類型
fun pow(a:Int)= Math.pow(a.toDouble(),2.toDouble());

定義局部變量

val聲明只讀變量:


//-------in kotlin---------
val a:Int=1
val b = 1
val c:Int //聲明
c=1
c=2//ERROR: val 只能賦值一次隧膏,這里會(huì)造成編譯錯(cuò)誤

//--------in java---------

final int a=1;

使用var聲明可變變量:

//--------in kotlin-------
var x=5
x+=1


//--------in java ---------
int x=5;

字符串模板

Kotlin允許在字符串中嵌入變量表達(dá)式

val name = "Bob"
println("My name is ${name}") //打印"My name is Bob"

val a = 10
val b = 20
println("The sum is ${a+b}") //打印"The sum is 30"

if 表達(dá)式

fun max(a: Int, b: Int): Int {
    if (a > b)
        return a
    else
        return b
}

fun max(a: Int,  b: Int) = if (a > b) a else b

when表達(dá)式

用于替代switch哗讥,但功能更強(qiáng)大

val age = 17

val typeOfPerson = when(age){
    0 -> "New born"
    in 1..12 -> "Child"
    in 13..19 -> "Teenager"
    else -> "Adult"
}

fun cases(obj: Any) {
    when (obj) {
        1 -> print("one")
        "hello" -> print("Greeting")
        is Long -> print("Long")
        !is Long -> print("Not a string")
        else -> print("Unknown")
    }
}

注:else是必須的嚷那,相當(dāng)于switch里的default

for 循環(huán)

fun sum(a: IntArray): Int {
    var sumValue=0;
    for(i in a){
        sumValue+=i
    }
    return sumValue
}
//或者

fun sum(a: IntArray): Int {
    var sumValue=0;
    for(i in a.indices){
        sumValue+=a[i]
    }
    return sumValue
}

while do ..while 循環(huán)

while和do..while循環(huán)的語(yǔ)法與Java完全相同胞枕。


fun sum(a: IntArray): Int {
    var sumValue=0;
    
    var i=0
    while(i<a.size){
        sumValue+=a[i++]
    }
    
    return sumValue
}

范圍表達(dá)式

在Kotlin中,范圍創(chuàng)建只需要..操作符魏宽,例如:

val r1 = 1..5
//該范圍包含數(shù)值1,2,3,4,5

如果創(chuàng)建一個(gè)降序范圍腐泻,則需要使用downTo函數(shù)

val r2 = 5 downTo 1
//該范圍包含數(shù)值5,4,3,2,1

默認(rèn)范圍的步長(zhǎng)是1,可使用step方法修改步長(zhǎng):

val r3 = 5 downTo 1 step 2
//該范圍包含數(shù)值5,3,1

可使用in操作符檢查數(shù)值是否在某個(gè)范圍內(nèi)

if (x in 1..y-1)
    print("OK")

范圍外:

if (x !in 0..array.lastIndex)
    print("Out")

Lambda

val sumLambda: (Int, Int) -> Int = {x,y -> x+y}

1.4 包

聲明包

package foo.bar

如果沒有指定包名队询,那這個(gè)文件的內(nèi)容就從屬于沒有名字的 "default" 包派桩。

import

import foo.Bar //Bar 現(xiàn)在可以不用條件就可以使用
//或者范圍內(nèi)的所有可用的內(nèi)容 (包,類蚌斩,對(duì)象铆惑,等等):
import foo.*/ /foo 中的所有都可以使用
//如果命名有沖突,我們可以使用 as 關(guān)鍵字局部重命名解決沖突
import foo.Bar // Bar 可以使用
import bar.Bar as bBar // bBar 代表 'bar.Bar'

1.5 控制流

if表達(dá)式

//傳統(tǒng)用法
var max = a
if (a < b)
    max = b

//帶 else 
var max: Int
if (a > b)
    max = a
else
    max = b

//作為表達(dá)式
val max = if (a > b) a else b

//if分之可以作為塊送膳,最后一個(gè)表達(dá)式是該塊的值

val max = if (a > b){
    print("Choose a")
    a
}
else{
    print("Choose b")
    b
}

when 表達(dá)式

一般用法,用來(lái)替代 switch,需注意的是里面的條件可以重復(fù),如果條件重復(fù)的話則按照最先匹配的條件處理

    val x=2
    when(x){
        1->print("1")
        2->print("2")
        else->print("else")//文檔里寫else 是mandatory的,但是去掉也可以
    }
    

如果多個(gè)條件的處理方式一樣,可以寫作下面方式:

when (x) {
    0,1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

可以用任意表達(dá)式作為分支的條件

when (x) {
    parseInt(s) -> print("s encode x")
    else -> print("s does not encode x")
}

可以用 in 或者 !in 檢查值是否值在一個(gè)集合中:

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")
}

可以使用 is 判斷是否是某個(gè)類型:

//官方的這個(gè)例子寫的很奇怪,感覺為了使用 when 來(lái)故意這么寫的

val hasPrefix = when (x) {
    is String -> x.startsWith("prefix")
    else -> false
}
//上面的例子可以直接寫成
val hasPrefix=x.startsWith("prefix")


//下面的例子可能更好一些
    var a=B()
        when(a){
            is C->print("A")
            is B->print("B")
            is A->print("C")
        }

    open class A{
        
    }
    open class B:A(){
        
    }
    class C:B(){
        
    }


when 也可以用來(lái)代替 if-else, 如果沒有提供任何參數(shù)則分支的條件就是表達(dá)式

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

測(cè)試?yán)?

fun main(args: Array<String>) {
    var x=3
    when(x){
        
        1->println("1->1")
        2->{
            x=3
            println("2->${x}")
        }
        3,4,5->println("3,4,5->")
        test()->println("test()->${test()}")
        in 7..10->println("6..10->${x}")
        }   
        
        var a=B()
        when(a){
            is C->print("A")
            is B->print("B")
            is A->print("C")
        }
        

}

open class A{
    
}
open class B:A(){
    
}
class C:B(){
    
}

fun test():Int{
    println("=====test====")
    return 6
}

for

for (item in collection)
    print(item)
    
for (i in array.indices)
    print(array[i])

while

//和 Java 一樣

返回與跳轉(zhuǎn)

標(biāo)簽

標(biāo)簽相當(dāng)于 C語(yǔ)言 中的 label,通過(guò)@結(jié)尾來(lái)表示,比如 abc@

loop@ for(i in 1..100){
    //
}

聲明了標(biāo)簽之后可以使用 break 或者 continue 進(jìn)行跳轉(zhuǎn):

  loop@ for (i in 1..10) {
        for (j in 1..10) {
            if (i == 5) {
                continue@loop
            }
        }
    }

2. 類

2.1 類

定義類

使用 class 關(guān)鍵字聲明類

class Invoice{
}

如果沒有類體可以省略大括號(hào)

class Empty

主構(gòu)造方法(primary constructor)

Kotlin 可以包含一個(gè)主要構(gòu)造方法和多個(gè)次要構(gòu)造方法,主構(gòu)造方法是class header 的一部分:跟在類名的后面(可選類型參數(shù))

class Person constructor(firstName: String) {
}

如果主構(gòu)造哦函數(shù)沒有注解或可見性說(shuō)明员魏,則 constructor 關(guān)鍵字是可以省略:

class Person(firstName: String){
}

主構(gòu)造不能包含任何代碼。初始化代碼可以放在初始化塊(initializer blocks)叠聋,初始化塊前綴使用init關(guān)鍵字:

class Customer(name: String) {
    init {
        logger.info("Customer initialized with value ${name}")
    }
}

注意撕阎,主構(gòu)造方法的參數(shù)可以在初始化塊中使用,也可以用在類的屬性初始化聲明處:

class Customer(name: String) {
    val customerKey = name.toUpperCase()
}

屬性的聲明可以寫在主構(gòu)造方法中:

class Person(val firstName: String, val lastName: String, var age: Int){
}

主構(gòu)造方法中的屬性可以是valvar
如果構(gòu)造函數(shù)有注解或可見性聲明,則 constructor 關(guān)鍵字是不可少的碌补,并且注解應(yīng)該在前:

class Customer public inject constructor (name: String) {...}

次要構(gòu)造方法(secondary constructors)

使用 constructor 關(guān)鍵字聲明次構(gòu)造方法

calss Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

如果顯示定義了主構(gòu)造方法則次構(gòu)造方法需要使用 this 關(guān)鍵字代理構(gòu)造方法

class Person(val name: String) {
    constructor (name: String, parent: Person) : this(name) {//此處需注意 次構(gòu)造方法里的 name 和 this(name)的 name 是同一個(gè),就是說(shuō)必須調(diào)用主構(gòu)造方法
        parent.children.add(this)
    }
}

如果一個(gè)非抽象類沒有聲明構(gòu)造方法,它會(huì)產(chǎn)生一個(gè)默認(rèn)的構(gòu)造方法,默認(rèn)是 public 的,如果不想有公共構(gòu)造方法,需要顯式聲明一個(gè)private構(gòu)造方法:

class DontCreateMe private constructor () {
}

屬性和字段

使用var聲明可變屬性,使用val聲明只讀屬性,屬性必須初始化,若定義的時(shí)候沒有初始化則必須在構(gòu)造方法里進(jìn)行初始化

Getters Setters

語(yǔ)法:

var <propertyName>: <PropertyType> [ = <property_initializer> ]//
    <getter>
    <setter>

一般用法:

var allByDefault: Int? // 錯(cuò)誤: 需要一個(gè)初始化語(yǔ)句, 默認(rèn)實(shí)現(xiàn)了 getter 和 setter 方法
var initialized = 1 // 類型為 Int, 默認(rèn)實(shí)現(xiàn)了 getter 和 setter

val simple: Int? // 類型為 Int 虏束,默認(rèn)實(shí)現(xiàn) getter 棉饶,但必須在構(gòu)造函數(shù)中初始化

val inferredType = 1 // 類型為 Int 類型,默認(rèn)實(shí)現(xiàn) getter

可以自定義getter和setter方法

var stringRepresentation: String
    get() = this.toString()
    set (value) {
        setDataFormString(value) // 格式化字符串,并且將值重新賦值給其他元素
}

///自定義getter方法,
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
    if (_table == null)
        _table = HashMap() //參數(shù)類型是推導(dǎo)出來(lái)的
        return _table ?: throw AssertionError("Set to null by another thread")
    }

注:setter方法里的參數(shù)可以自定義名稱

Backing Fileds

在Kotlin里沒有字段,但提供了Backing Fields 機(jī)制,備用字段使用field關(guān)鍵字聲明,field關(guān)鍵詞只能用于屬性的訪問(wèn)器

var counter = 0 //在初始化中直接寫入備用字段
    set(value) {
        if (value >= 0)
            field  = value
    }
//如果counter賦值給負(fù)數(shù)則counter值為0


屬性延遲初始化

非空屬性必須在定義的時(shí)候初始化,kotlin提供了一種可以延遲初始化的方案,使用lateinit關(guān)鍵字描述屬性


class LazyProperty(val initializer: () -> Int) {
    var value: Int? = null
    val lazy: Int
        get() {
            if (value == null) {
                value = initializer()
            }
            return value!!
        }
}

方法

方法聲明

使用關(guān)鍵字fun來(lái)聲明成員方法:

fun double(x: Int): Int {

}

方法調(diào)用

//通過(guò)傳統(tǒng)的方法調(diào)用函數(shù)
val result = double(2)

//通過(guò).調(diào)用成員函數(shù)
Sample().foo() // 創(chuàng)建Sample類的實(shí)例,調(diào)用foo方法

中綴符號(hào)

成員方法或者擴(kuò)展方法,只有一個(gè)參數(shù)使用infix關(guān)鍵詞描述

/給 Int 定義一個(gè)擴(kuò)展方法
infix fun Int.shl(x: Int): Int {
...
}
println(1 shl 2) //用中綴注解調(diào)用擴(kuò)展函數(shù)

輸出4

參數(shù)

參數(shù)基本格式name:type,參數(shù)之間使用逗號(hào)隔開,每個(gè)參數(shù)必須指明類型

fun powerOf(number: Int, exponent: Int) {
...
}

默認(rèn)參數(shù)

參數(shù)可以設(shè)置默認(rèn)值,參數(shù)被忽略時(shí)候會(huì)使用默認(rèn)值,這樣可以減少重載方法的定義

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}

參數(shù)命名

調(diào)用參數(shù)的時(shí)候可以對(duì)參數(shù)命名

fun reformat(str: String, normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true,
             divideByCamelHumps: Boolean = false,
             wordSeparator: Char = ' ') {
...
}

reformat(str,
    normalizeCase = true,
    uppercaseFirstLetter = true,
    divideByCamelHumps = false,
    wordSeparator = '_'
  )

注意:命名參數(shù)語(yǔ)法不能用在調(diào)用Java方法中,因?yàn)镴ava字節(jié)碼不能確保方法參數(shù)命名的不變性.

單表達(dá)式函數(shù)

當(dāng)函數(shù)只包含單個(gè)表達(dá)式時(shí),大括號(hào)可以省略并在=后面定義函數(shù)


fun double(x:Int):Int=x*2

在編譯器可以推斷出返回值類型的時(shí)候,返回值的類型可以省略:

fun double(x: Int) = x * 2

變長(zhǎng)參數(shù)

函數(shù)的變長(zhǎng)參數(shù)可以用vararg 關(guān)鍵字進(jìn)行標(biāo)識(shí)

fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}

//調(diào)用方法:

    vars(1,2,3,4,5)//輸出12345

注意 只有一個(gè)參數(shù)可以被標(biāo)注為vararg ,如果可變長(zhǎng)度參數(shù)不是最后一個(gè)參數(shù),則后面的參數(shù)需要通過(guò)命名參數(shù)語(yǔ)法進(jìn)行傳值,如果參數(shù)是函數(shù)類型,則需要通過(guò)lambda傳遞.

*前綴操作符

調(diào)用變長(zhǎng)參數(shù)的函數(shù)時(shí),可以將傳入一個(gè)同參數(shù)類型一致的數(shù)組來(lái)引用數(shù)組內(nèi)容位可變參數(shù)內(nèi)容


val a = array(1, 2, 3)
val list = asList(-1, 0, *a, 4)

局部方法

kotlin支持在一個(gè)函數(shù)中包含另一個(gè)函數(shù)(同JS)


fun dfs(graph: Graph) {
  fun dfs(current: Vertex, visited: Set<Vertex>) {
    if (!visited.add(current)) return
    for (v in current.neighbors)
      dfs(v, visited)
  }

  dfs(graph.vertices[0], HashSet())
}

局部方法可以訪問(wèn)外部方法的局部變量(類似java匿名內(nèi)部類)

fun dfs(graph: Graph) {
  val visited = HashSet<Vertex>()
  fun dfs(current: Vertex) {
    if (!visited.add(current)) return
    for (v in current.neighbors)
      dfs(v)
  }

  dfs(graph.vertices[0])
}

成員函數(shù)

成員函數(shù)是定義在一個(gè)類或者對(duì)象里的

class Sample() {
    fun foo() { print("Foo") }
}

泛型函數(shù)

具體內(nèi)容請(qǐng)看 泛型部分

尾遞歸函數(shù)

這個(gè)概念在ES6里有同樣定義.一種避免棧溢出的方法,當(dāng)函數(shù)被標(biāo)記為tailRecursive時(shí),編譯器會(huì)優(yōu)化遞歸,用循環(huán)代替


tailRecursive fun findFixPoint(x: Double = 1.0): Double 
    = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
    
//等效于下面的代碼:

private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if (x == y) return y
        x = y
    }
}

使用 tailrec 修飾符必須在最后一個(gè)操作中調(diào)用自己。在遞歸調(diào)用代碼后面是不允許有其它代碼的镇匀,并且也不可以在 try/catch/finall 塊中進(jìn)行使用照藻。當(dāng)前的尾遞歸只在 JVM 的后端中可以用

接口定義

接口使用 interface 關(guān)鍵字聲明,可以包含抽象方法和方法的實(shí)現(xiàn),這點(diǎn)類似于 Java 8的 Default interface

interface MyInterface {
    fun bar()
    fun foo() {
      // optional body
    }
}



泛型

接口繼承

class Child : MyInterface {
   override fun bar() {
      // body
   }
}

接口中的屬性

可以在接口中定義屬性,接口中的屬性可以是抽象的也可以實(shí)現(xiàn)訪問(wèn)器,但是不能定義 backing field.

interface MyInterface {
    val property: Int // abstract

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(property)
    }
}

class Child : MyInterface {
    override val property: Int = 29
}

抽象類

使用abstract關(guān)鍵字來(lái)描述一個(gè)抽象類,抽象類里的方法如果不提供實(shí)現(xiàn)則需要用abstract關(guān)鍵字來(lái)描述抽象方法.抽象的方法默認(rèn)是open的


abstract class A {
    abstract fun f()
}

interface B {
    open fun f() { print("B") }
}

class C() : A() , B {
    //我們是必須重寫 f() 方法
}

密封類(Sealed Classes)

概念比較難理解,和 C#中的概念不一樣,詳細(xì)的內(nèi)容可以看這篇文檔

聲明一個(gè)密封類,使用sealed修飾類坑律,密封類可以有子類岩梳,但是所有的子類都必須要內(nèi)嵌在密封類中。

sealed 不能修飾 interface ,abstract class(會(huì)報(bào) warning,但是不會(huì)出現(xiàn)編譯錯(cuò)誤)

官方例子:

//PS 這例子...
sealed class Expr {
    class Const(val number: Double) : Expr()
    class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
}

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
    // the `else` clause is not required because we've covered all the cases
}

繼承

Kotlin里所有類都繼承于父類Any,相當(dāng)于Object

class Example

Kotlin 里類默認(rèn)都是final的,如果聲明的類需要被繼承則需要使用open 關(guān)鍵字來(lái)描述類

open class Base(p: Ont)

class Derived(p: Int) : Base(p)

如果類沒有主構(gòu)造函數(shù)


class MyView : View {
    constructor(ctx: Context) : super(ctx) {
    }
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
    }
}

如果父類只存在默認(rèn)構(gòu)造方法

open class A{
    
}
//形式1

class B:A(){
    
}
//形式2
class C:A{
    constructor():super();
}


重寫方法

在子類里重寫父類的方法需要使用override關(guān)鍵字描述方法


open class A{
    open fun me(){}
    fun me1(){}
}

class B:A(){
    //override fun me1(){} Error: 'me' in 'A' is final and cannot be overridden
    override fun me(){}
}

如果一個(gè)類從它的直接父類繼承了同一個(gè)成員的多個(gè)實(shí)現(xiàn)晃择,那么它必須重寫這個(gè)成員并且提供自己的實(shí)現(xiàn)

open class A {
    open fun f () { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } //接口的成員變量默認(rèn)是 open 的
    fun b() { print("b") }
}

class C() : A() , B{
    override fun f() {
        super<A>.f()//調(diào)用 A.f()
        super<B>.f()//調(diào)用 B.f()
    }
}

如果類C中沒有定義f() 編譯器會(huì)報(bào)如下錯(cuò)誤:
Error:(29, 0) Class 'C' must override public open fun f(): kotlin.Unit defined in A because it inherits many implementations of it

作用域修飾符

對(duì)象表達(dá)式

F#和JS都里有這個(gè)類似的定義

對(duì)象表達(dá)式是一個(gè)表達(dá)式冀值,可用于創(chuàng)建基于現(xiàn)有的基類型、接口或接口集動(dòng)態(tài)創(chuàng)建的匿名對(duì)象類型的新實(shí)例宫屠。

對(duì)象表達(dá)式

我們通過(guò)下面這樣的方式創(chuàng)建繼承自某種(或某些)匿名類的對(duì)象:

window.addMouseListener(object: MouseAdapter () {
    override fun mouseClicked(e: MouseEvent) {
        //...
    }
})

如果父類有構(gòu)造函數(shù)列疗,則必須傳遞相應(yīng)的構(gòu)造函數(shù)。多個(gè)父類可以用逗號(hào)隔開浪蹂,跟在冒號(hào)后面

open class A(x: Int) {
    public open val y: Int = x
}

interface B { ... }

val ab = object : A(1), B {
    override val y = 14
}


有時(shí)候我們只是需要一個(gè)沒有父類的對(duì)象抵栈,我們可以這樣寫:



val adHoc = object {
    var x: Int = 0
    var y: Int = 0
}

print(adHoc.x + adHoc.y)

像 java 的匿名內(nèi)部類一樣,對(duì)象表達(dá)式可以訪問(wèn)閉合范圍內(nèi)的變量 (和 java 不一樣的是坤次,這些不用聲明為 final)

fun countClicks(windows: JComponent) {
    var clickCount = 0
    var enterCount = 0
    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++//此處需注意,java里匿名內(nèi)部類是無(wú)法修改方法棧里的變量
        }
        override fun mouseEntered(e: MouseEvent){
            enterCount++
        }
    })
}

對(duì)象聲明

聲明單例模式:

object DataProviderManager {
    fun registerDataProvider(provider: Dataprovider) {
        //...
    }
    val allDataProviders : Collection<DataProvider>
        get() = //...
}

Extensions

Kotlin可以在不繼承父類的情況下對(duì)一個(gè)類增加一個(gè)新的方法,這種方法叫做Extensions.
Kotlin支持?jǐn)U展方法和擴(kuò)展屬性

擴(kuò)展方法

為了聲明一個(gè)擴(kuò)展函數(shù)古劲,我們需要在函數(shù)名使用接收者類型作為前綴

fun MutableList<Int>.swap(x: Int, y: Int) {
    val temp = this[x] // this 對(duì)應(yīng) list
    this[x] = this[y]
    this[y] = tmp
}

擴(kuò)展方法中的this關(guān)鍵字對(duì)應(yīng)被擴(kuò)展對(duì)象

val l = mutableListOf(1, 2, 3)
l.swap(0, 2)

靜態(tài)解析擴(kuò)展

擴(kuò)展方法是靜態(tài)分發(fā)的,不會(huì)動(dòng)態(tài)根據(jù)類型來(lái)進(jìn)行方法調(diào)用.

open class C

class D: C()

fun C.foo() = "c"

fun D.foo() = "d"

fun printFoo(c: C) {
    println(c.foo())
}

printFoo(D())//此處的值為c




/////對(duì)比一下多態(tài)下的方法調(diào)用


open class A{
    open fun foo():String{
        return "a"
    }
}
class B:A() {
    override fun foo():String{
        return "b"
    }
}

fun printClass(a:A){
    println(a.foo())
}


printClass(B())//此處輸出b


如果擴(kuò)展的方法和成員方法名稱和參數(shù)一樣,則按照成員方法調(diào)用,不會(huì)調(diào)用擴(kuò)展方法

class C {
    fun foo() { println("member") }
}

fun C.foo() { println("extension") }

擴(kuò)展屬性

語(yǔ)法結(jié)構(gòu)如下 : val 類名.屬性名[:屬性類型] get() set()

擴(kuò)展屬性只能通過(guò)getter和setter方法來(lái)進(jìn)行定義:

val <T> List<T>.lastIndex:  Int
    get() = size-1
    
    
///    
val Foo.bar = 1 //error: initializers are not allowed for extension properties

創(chuàng)建類實(shí)例

val invoice = Invoice()
val customer = Customer("Joe Smith")

注意Kotlin沒有new關(guān)鍵字

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缰猴,隨后出現(xiàn)的幾起案子产艾,更是在濱河造成了極大的恐慌,老刑警劉巖滑绒,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闷堡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡疑故,警方通過(guò)查閱死者的電腦和手機(jī)杠览,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纵势,“玉大人踱阿,你說(shuō)我怎么就攤上這事∏仗” “怎么了软舌?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)育瓜。 經(jīng)常有香客問(wèn)我葫隙,道長(zhǎng),這世上最難降的妖魔是什么躏仇? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上藻雌,老公的妹妹穿的比我還像新娘。我一直安慰自己怀喉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布船响。 她就那樣靜靜地躺著躬拢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪见间。 梳的紋絲不亂的頭發(fā)上聊闯,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音米诉,去河邊找鬼菱蔬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛史侣,可吹牛的內(nèi)容都是我干的拴泌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼惊橱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚪腐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起税朴,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤回季,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后掉房,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茧跋,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慰丛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年卓囚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诅病。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哪亿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贤笆,到底是詐尸還是另有隱情蝇棉,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布芥永,位于F島的核電站篡殷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏埋涧。R本人自食惡果不足惜板辽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一奇瘦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧劲弦,春花似錦耳标、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至画畅,卻和暖如春砸琅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轴踱。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工明棍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寇僧。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓摊腋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嘁傀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兴蒸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容