Kotlin基礎語法篇

自從實習結束後直到如今將近一年多的時間再也沒有用過kotlin, 在今年五月份I/O大會上,Google再次明確了Kotlin在Android開發中的地位,並毫無懸念的將這個「後生晚輩」定爲官方「首選」語言,這使得Kotlin不得不被「傳統java 開發者」重視,他們是時候改擁抱變化了,因此咱們項目主要技術負責人如今勉強接受你們使用Kotlin開發意願(實在不容易~)。java

對荒廢了一年的語言如今從新拾起感受又熟悉又陌生,但心裏仍是很興奮的,最近準備系統地整理一下Kotlin相關內容, 但願能在短期內從新掌握,今天主要從新梳理一下Kotlin基礎語法。編程

Kotlin語言介紹

  • Kotlin 是 JetBrains 在 2010 年推出的基於 JVM 的新編程語言,是一種新的靜態類型編程語言。開發者稱,設計它的目的是避免 Java 語言編程中的一些難題。好比:在 Kotlin 中類型系統控制了空指針引用,能夠有效避免 Java 中常見的NullPointException。
  • 相比於 Java,Kotlin 有着更好的語法結構,安全性和開發工具支持。
    Kotlin 中沒有基礎類型,數組是定長的,泛型是安全的,即使運行時也是安全的。此外,該語言支持閉包,還可經過內聯進行優化。不過,它不支持檢查異常(Checked Exceptions),許多語言設計者認爲這是它的瑕疵。不論如何,重要的是 Java 和 Kotlin 之間的互操做性:Kotlin 能夠調用 Java,反之亦可。
  • Kotlin基於JVM的字節碼結構(與java同屬靜態語言),Kotlin 應用程序的運行速度與 Java 相似。,但隨着 Kotlin 對內聯函數的支持,使用 lambda 表達式的代碼一般比用 Java 寫的代碼運行得更快。
  • Kotlin 可與 Java 進行 100% 的互操做,容許在 Kotlin 應用程序中使用全部現有的 Android 庫 ,這包括註解處理,Google官方也說過將來Android支持庫優先支持Kotlin語言;
  • Kotlin屬於跨平臺編譯語言,除了能夠進行原聲開發外很好的支持Java服務端程序開發、Android應用程序開發以及JavaScript開發;

Kotlin安裝與配置這裏省略(網上不少教程,AndroidStudio3.x版本默認已支持Kotlin插件)數組

  1. 基礎數據類型

       Kotlin 的基本數據類型包括 Byte、Short、Int、Float、Long、Double 、Boolean, 在java中基本數據類型一共有八種分別包括整形:byte、short、int、long,浮點型:float、double , 字符型 char 以及布爾型 boolean,不一樣於 Java 的是,Kotlin 字符不屬於數值類型,是一個獨立的數據類型, 你們能夠看出Kotlin的基本數據類型和java不同,Kotlin 中其實沒有基礎數據類型,只有封裝的對象類型,你每定義的一個變量,其實 Kotlin 幫你封裝了一個對象,這樣能夠保證不會出現空指針, 對應Java中八大基本數據類型的對象類型、java在使用基礎數據類型時候能夠經過裝箱操做封裝成對象;安全

  • 字符
  • 和 Java 不同,Kotlin 中的 Char 不能直接和數字操做(能夠經過顯示轉換char.toInt()),Char 必需是單引號 ' 包含起來的。好比普通字符 '0','a',字符字面值用單引號括起來: '1'。 特殊字符能夠用反斜槓轉義。 支持這幾個轉義序列:\t、 \b、\n、\r、\'、\"、\\ 和 \$。

當須要可空引用時,像數字、字符會被裝箱。裝箱操做不會保留同一性。bash

  • 布爾

       布爾用 Boolean 類型表示,它有兩個值:true 和 false。當須要可空引用布爾會被裝箱。閉包

  • 數組

      數組用Array類實現,具備size屬性、get、setf方法,因爲使用 [] 重載了 get 和 set 方法,因此咱們能夠經過下標很方便的獲取或者設置數組對應位置的值。java中素組具備length屬性以及使用[]經過下標方式訪問屬性;app

數組的建立兩種方式:一種是使用函數arrayOf();另一種是使用工廠函數。以下所示,咱們分別是兩種方式建立了兩個數組:編程語言

fun main(args: Array<String>) {
    //[1,2,3]
    val a = arrayOf(1, 2, 3)

    //讀取數組內容
    println(a[0])    // 輸出結果:1
    println(b[1])    // 輸出結果:2
}


val x: IntArray = intArrayOf(1, 2, 3
複製代碼

除了類Array,還有ByteArray, ShortArray, IntArray,LongArray等用來表示各個類型的數組,省去了裝箱操做,所以效率更高,其用法同Array同樣:ide

  • 字符串

和java同樣,String屬於不可變的,Kotlin能夠經過[]很方便訪問對應下標字符,java中經過chatAt方法或者subString等方式獲取對應字符,Kotlin中String支持遍歷形式訪問其中的字符,這一點很使用;函數

for (c in str) {
    println(c)
}複製代碼

另外Kotlin 支持三個引號 """ 擴起來的字符串,支持多行字符串,好比:

fun main(args: Array<String>) {
    val text = """多行字符串 多行字符串"""
    println(text)   // 輸出
}複製代碼

String 能夠經過 trim(),trimEnd(),trimStart(),trimMargin() 等方法來刪除多餘的空白。

  • 類型轉換

java中能夠經過隱式類型轉換,數值大的類型能夠轉換成數據小的類型,可是這樣每每會丟失精度,在kotlin中因爲不一樣的表示方式,較小類型並非較大類型的子類型,較小的類型不能隱式轉換爲較大的類型。 這意味着在不進行顯式轉換的狀況下咱們不能把 Byte 型值賦給一個 Int 變量,

val b: Byte = 1 // OK, 字面值是靜態檢測的
val i: Int = b.toInt() // OK複製代碼

每種數據類型都有下面的這些方法,能夠轉化爲其它的類型:

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

2. 變量、常量聲明

  • 定義只讀變量使用關鍵字 val 定義,只能爲其賦值一次,
val a: Int = 1  // 當即賦值 (非空屬性必須在定義時候初始化,)
val b = 2   // 自動推斷出 `Int` 類型  (非空屬性必須在定義時候初始化,)

複製代碼
  • 可從新賦值的變量使用 var 關鍵字:
var x = 5 // 自動推斷出 `Int` 類型 (非空屬性必須在定義時候初始化,)
x += 1

複製代碼

Kotlin語法支持類型自動推斷,在聲明變量或者常量的時候能夠不用指定其類型,編譯器在編譯時候會爲咱們指定其類型;

非空屬性必須在定義的時候初始化,kotlin提供了一種能夠延遲初始化的方案,使用 lateinit 關鍵字描述屬性:

var count:Int?=null //可空屬性能夠在後面賦值 
count=5複製代碼

lateinit var name : String 非空屬性使用延遲初始化

  • 函數定義

函數定義使用fun關鍵字,參數格式爲 參數:類型 ,最後函數返回值類型,以下

fun sum( a:Int, b:Int):Int{return a+b}複製代碼

亦能夠函數表達式聲明函數

fun sum( a:Int, b:Int)=  a+b  // 自動類型推斷或者 fun sum(a:Int,b:Int):Int=a+b  複製代碼

無返回值的函數定義(相似Java中的void):

fun printSum(a: Int, b: Int): Unit { 
    print(a + b)
}

複製代碼

Unit 返回類型能夠省略:

public fun printSum(a: Int, b: Int) { 
    print(a + b)
}
複製代碼

3. 可變長參數函數

可變長參數用vararg關鍵字進行修飾:

fun print(vararg v:Int){   
        for(a in v){   
            println("$a")
        }
} 複製代碼

4. 字符串模板

$ 表示一個變量名或者變量值

$varName 表示變量值

${varName.fun()} 表示變量的方法返回值:

var a = 1
// 模板中的簡單名稱:
val s1 = "a is $a" 
a = 2

// 模板中的任意表達式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

// 運行結果:a was 1, but now is 2複製代碼

5. 條件表達式

看一個經常使用if表達式:

fun value(a:Int, b :Int):Int { 
  if(a>b) { 
    return a+b  
  }else{   
    return a-b     
  }
}複製代碼

經過條件表達式能夠:

fun value(a:Int,b:Int)=if(a>b) a+b else a-b

6. NULL檢查機制

Kotlin的空安全設計對於聲明可爲空的參數,在使用時要進行空判斷處理,有兩種處理方式,字段後加!!像Java同樣拋出空異常,另外一種字段後加?可不作處理返回值爲 null或配合?:作空判斷處理

//類型後面加?表示可爲空
var age: String? = "23" 
//拋出空指針異常
val ages = age!!.toInt()
//不作處理返回 null
val ages1 = age?.toInt()
//age爲空返回-1
val ages2 = age?.toInt() ?: -1複製代碼

當一個引用可能爲 null 值時, 對應的類型聲明必須明確地標記爲可爲 null。

Kotlin 的NULL機制旨在消除來自代碼空引用的危險,這在java語言中屬於最多見的陷阱之一,也就是訪問空引用的成員會致使空引用異常。常常會拋出 NullPointerException 或簡稱 NPE

在Kotlin中常常會有一些鏈式調用用法,安全調用在鏈式調用中頗有用,如:person?.class?.countName 若是調用鏈中任何一個屬性值出現null狀況,調用鏈會直接返回null,

後面屬性不會出現NPE異常。

若是相對非null值執行某個操做,能夠結合let操做符一塊兒使用:

val listWithNulls: List<String?> = listOf("Kotlin", null)      
for (item in listWithNulls) {
           item?.let {   
           println(it) // 輸出 Kotlin 並忽略 null  
    }
 }複製代碼

一樣安全調用也能夠出如今賦值的左側,若是調用鏈中的任何一個接收者爲空都會跳過賦值,而右側的表達式根本不會求值:

person?.class?.mathTeacher = TeachManager.getTeacher()複製代碼

7. Elvis 操做符

當咱們有一個可空的引用 r 時,咱們能夠說「若是 r 非空,我使用它;不然使用某個非空的值 x」:

val l: Int = if (b != null) b.length else -1

或者

fun value(a:Int,b:Int)=if(a>b) a+b else a-b
//條件表達式用法
複製代碼

除了完整的 if-表達式,這還能夠經過 Elvis 操做符表達,寫做 ?:

val l = b?.length ?: -1複製代碼

若是 ?: 左側表達式非空,elvis 操做符就返回其左側表達式,不然返回右側表達式。 請注意,當且僅當左側爲空時,纔會對右側表達式求值。

這種Elvis用法相似java語言中三元操做符;

public int value(int a,int b){return a>b?a+b :a-b}
等同於:
fun value(a:Int,b:Int)=if(a>b) a+b else a-b

複製代碼
val l = b?.length ?: -1

等同於:

final int l= b!=null? b.length : -1 複製代碼

8. 類型檢測及類型轉換

若是對象不是目標類型,那麼常規類型轉換可能會致使 ClassCastException

val aInt: Int? = a as Int複製代碼

爲了不類型轉成異常,另外一個選擇是使用安全的類型轉換,若是嘗試轉換不成功則返回 null:

val aInt: Int? = a as? Int
或者
val sInt :Int?= if(a is Int) a as Int else null
或者
val sInt :Int?= if(a is Int) a else null複製代碼

is 運算符檢測一個表達式是否某類型的一個實例(相似於Java中的instanceof關鍵字), 若是一個不可變的局部變量或屬性已經判斷出爲某類型,那麼檢測後的分支中能夠直接看成該類型使用,無需顯式轉換:

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // 作過類型判斷之後,obj會被系統自動轉換爲String類型
    return obj.length 
  }

  // 這裏的obj仍然是Any類型的引用
  return null
}

或者

fun getStringLength(obj: Any): Int? {
  if (obj !is String)
    return null
  // 在這個分支中, `obj` 的類型會被自動轉換爲 `String`
  return obj.length
}複製代碼

9. 可空類型的集合

若是你有一個可空類型元素的集合,而且想要過濾掉空元素,你可使用 filterNotNull 來實現:

val listOf = listOf<Int?>(1, 2, null, 4)複製代碼
val intList: List<Int> = listOf.filterNotNull()複製代碼

10. 區間

區間表達式由具備操做符形式 .. 的 rangeTo 函數輔以 in 和 !in 造成。

使用 in 運算符來檢測某個數字是否在指定區間內:

for (i in 1..4) print(i) // 輸出「1234」

for (i in 4..1) print(i) // 什麼都不輸出

if (i in 1..10) { // 等同於 1 <= i && i <= 10
    println(i)
}

// 使用 step 指定步長
for (i in 1..4 step 2) print(i) // 輸出「13」

for (i in 4 downTo 1 step 2) print(i) // 輸出「42」

// 使用 until 函數排除結束元素
for (i in 1 until 10) {   // i in [1, 10) 排除了 10
     println(i)
}


val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
    println("-1 is out of range")
}
if (list.size !in list.indices) {
    println("list size is out of valid list indices range, too")
}

複製代碼

11. 使用 for 循環

for 循環能夠對任何提供迭代器(iterator)的對象進行遍歷,語法以下:

一樣,kotlin的for循環中使用的也是in操做符,

val items = listOf("dog", "cat", "pig")
for (item in items) { 
 println(item)
}

複製代碼

或者經過索引

val items = listOf("apple", "banana", "kiwifruit")

for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

val array=arrayOf("a","b","c")
for (i in array.indices) {
    print(array[i])
}複製代碼

12. 使用 when 表達式

when 將它的參數和全部的分支條件順序比較,直到某個分支知足條件,

when 既能夠被當作表達式使用也能夠被當作語句使用。若是它被當作表達式,符合條件的分支的值就是整個表達式的值,若是當作語句使用, 則忽略個別分支的值。

when 相似java語言的 switch 操做符。其最簡單的形式以下:

fun describe(obj: Any): String =
when (obj) {
 1          -> "One" 
"Hello"    -> "Greeting" 
is Long    -> "Long" 
!is String -> "Not a string"
else       -> "Unknown"
}
複製代碼

在Kotlin中Any類是全部類的超類,相似java中的Object;

在 when 中,else 同 switch 的 default。若是其餘分支都不知足條件將會求值 else 分支。若是不少分支須要用相同的方式處理,則能夠把多個分支條件放在一塊兒,用逗號分隔。

when 也能夠用來取代 if-else if鏈。 若是不提供參數,全部的分支條件都是簡單的布爾表達式,而當一個分支的條件爲真時則執行該分支:

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

13. 使用 lambda 表達式來過濾(filter)與映射(map)集合

val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits.filter { it.startsWith("a") } 
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }

結果:APPLE AVOCADO
複製代碼

14. 類以及構造器

類的修飾符包括 classModifier 和_accessModifier_:

  • classModifier: 類屬性修飾符,標示類自己特性。

    abstract    // 抽象類  
    final       // 類不可繼承,默認屬性
    enum        // 枚舉類
    open        // 類可繼承,類默認是final的
    annotation  // 註解類複製代碼
  • accessModifier: 訪問權限修飾符

    private    // 僅在同一個文件中可見
    protected  // 同一個文件中或子類可見
    public     // 全部調用的地方均可見
    internal   // 同一個模塊中可見複製代碼
  • 主構造器(主構造函數)

Koltin 中的類能夠有一個 主構造器,以及一個或多個次構造器,主構造器是類頭部的一部分,位於類名稱以後:

class Person constructor(firstName: String) {}複製代碼

若是主構造器沒有任何註解,也沒有任何可見度修飾符,那麼constructor關鍵字能夠省略。

class Person(firstName: String) {
}複製代碼

主構造器中不能包含任何代碼,初始化代碼能夠放在初始化代碼段中,初始化代碼段使用 init 關鍵字做爲前綴。

class Person constructor(firstName: String) {
    init {
       firstName="Scus"  //主構造器的參數能夠在初始化代碼段中使用
    }
}複製代碼

另外能夠經過主構造器來定義屬性並初始化屬性值(能夠是var或val):

class People(val firstName: String, val lastName: String) {
    //...
}複製代碼

若是一個非抽象類沒有聲明構造函數(主構造函數或次構造函數),它會產生一個沒有參數的構造函數。構造函數是 public 。若是你不想你的類有公共的構造函數,你就得聲明一個空的主構造函數:

class DontCreateMe private constructor () {
}複製代碼
  • 次構造函數

類也能夠有二級構造函數,須要加前綴 constructor:

class Person(val name: String) {
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
}複製代碼
  • 抽象類

抽象是面向對象編程的特徵之一,類自己,或類中的部分紅員,均可以聲明爲abstract的。抽象成員在類中不存在具體的實現

注意:無需對抽象類或抽象成員標註open註解。

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}複製代碼
  • 嵌套類

咱們能夠把類嵌套在其餘類中,看如下實例:

class Outer {                  // 外部類
    private val bar: Int = 1
    class Nested {             // 嵌套類
        fun foo() = 2
    }
}複製代碼
  • 匿名內部類

使用對象表達式來建立匿名內部類:

test.setInterFace(object : TestInterFace {
        override fun test() {
            println("對象表達式建立匿名內部類的實例")
        }
    })
複製代碼
  • 內部類

內部類使用 inner 關鍵字來表示。

內部類會帶有一個對外部類的對象的引用,因此內部類能夠訪問外部類成員屬性和成員函數。

class Outer {
    private val bar: Int = 1
    var v = "成員屬性"
    /**嵌套內部類**/
    inner class Inner {
        fun foo() = bar  // 訪問外部類成員
        fun innerTest() {
            var o = this@Outer //獲取外部類的成員變量
            println("內部類能夠引用外部類的成員,例如:" + o.v)
        }
    }
}
複製代碼
  • 數據類

Kotlin 能夠建立一個只包含數據的類,關鍵字爲 data:

data class User(val name: String, val age: Int)複製代碼
  • 密封類

密封類用來表示受限的類繼承結構:當一個值爲有限幾種的類型, 而不能有任何其餘類型時。在某種意義上,他們是枚舉類的擴展:枚舉類型的值集合 也是受限的,但每一個枚舉常量只存在一個實例,而密封類 的一個子類能夠有可包含狀態的多個實例。

聲明一個密封類,使用 sealed 修飾類,密封類能夠有子類,可是全部的子類都必需要內嵌在密封類中。

sealed class Expr
data class Const(val number: Double) : Expr()
data 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
}複製代碼

15. 接口

  • Kotlin 接口與 Java 8 相似,使用 interface 關鍵字定義接口,容許方法有默認實現:
interface MyInterface {
     fun bar() // 未實現     
     fun foo() {//已實現       
        println("foo")  
    }
}複製代碼
  • 一個類或者對象能夠實現一個或多個接口。
class Child : MyInterface {
    override fun bar() {
        // 方法體
    }
    override fun fool() {
        // 方法體
    }
}複製代碼
  • 接口中的屬性只能是抽象的,不容許初始化值,接口不會保存屬性值,實現接口時,必須重寫屬性:
interface MyInterface{
    var name:String //name 屬性, 抽象的
}
 
class MyImpl:MyInterface{
    override var name: String = "runoob" //重寫屬性
}複製代碼

16. Kotlin 繼承

kotlin 中全部類都繼承該 Any 類,它是全部類的超類,對於沒有超類型聲明的類是默認超類:

若是一個類要被繼承,可使用 open 關鍵字進行修飾。

open class Base(p: Int)           // 定義基類

class Derived(p: Int) : Base(p)複製代碼
注意:Any 不是 java.lang.Object。

若是子類有主構造函數, 則基類必須在主構造函數中當即初始化。

open class Person(var name : String, var age : Int){// 基類

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}複製代碼

若是子類沒有主構造函數,則必須在每個二級構造函數中用 super 關鍵字初始化基類,或者在代理另外一個構造函數。初始化基類時,能夠調用基類的不一樣構造方法。

class Student : Person {

    constructor(ctx: Context) : super(ctx) {
    } 

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
    }
}複製代碼

在基類中,使用fun聲明函數時,此函數默認爲final修飾,不能被子類重寫。若是容許子類重寫該函數,那麼就要手動添加 open 修飾它,。

17.對象聲明

Kotlin 使用 object 關鍵字來聲明一個對象。

Kotlin 中咱們能夠方便的經過對象聲明來得到一個單例。

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}複製代碼

引用該對象,咱們直接使用其名稱便可:

DataProviderManager.registerDataProvider(……)複製代碼

固然你也能夠定義一個變量來獲取獲取這個對象,當你定義兩個不一樣的變量來獲取這個對象時,你會發現你並不能獲得兩個不一樣的變量,也就是說經過這種方式,咱們得到一個單例:如

object Site {
    var url:String = ""
    val name: String = "菜鳥教程"
}
fun main(args: Array<String>) {
    var s1 =  Site
    var s2 = Site
    s1.url = "www.runoob.com"
    println(s1.url)
    println(s2.url)
}
// 輸出結果都爲"www.runnoob.com"
複製代碼

18.伴生對象

類內部的對象聲明能夠用 companion 關鍵字標記,這樣它就與外部類關聯在一塊兒,咱們就能夠直接經過外部類訪問到對象的內部元素。

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

val instance = MyClass.create()   // 訪問到對象的內部元素複製代碼

咱們能夠省略掉該對象的對象名,而後使用 Companion 替代須要聲明的對象名:

class MyClass {
    companion object {
    }
}
複製代碼

注意:一個類裏面只能聲明一個內部關聯對象,即關鍵字 companion 只能使用一次。

伴生對象的成員看起來像java的靜態成員,但在運行時他們仍然是真實對象的實例成員。

相關文章
相關標籤/搜索