kotlin语法概览

kotlin 是用于现代多平台应用的静态编程语言,可以与 Java™ 和 Android™ 100% 可互操作。

基本语法

表达式和语句

表达式:有值,并且能作为另一个表达式的一部分使用。
语句:总是包含着它的代码块中的顶层元素,并且没有自己的值。
当函数体是由单个表达式构成时,可以用这个表达式作为完整的函数体,并且去掉花括号和 return 语句

    fun max(x: Int, y: Int): Int {
        return if (x > y) x else y
    }

    fun min(x: Int, y: Int) = if (x > y) y else x

可变变量和不可变变量 : var 和 val

使用 val 声明的变量不能在初始化之后再次赋值
这种变量的值可以改变,但是它的类型却是改变不了的

属性

当声明属性的时候,就声明了对应的访问器(只读属性有一个 gettter,而可变属性则有 getter/setter)

class Person(
        val name: String,
        var isMarried: Boolean
)
fun personFun() {
    val person = Person("andy", true)
    print("${person.name}是不是${person.isMarried}")
}
class Rectangle(var width: Int, var height: Int) {
    val isSquare = width == height
}
fun main(args: ArrayList<String>) {
    personFun()
    val rectangle = Rectangle(12, 12)
    print(rectangle.isSquare)
}

field

class LengthCounter {
    var str: String = ""
        set(value) {
            if (value == "") {
                println("no value")
            } else {
                field = value
            }
        }
    var counter = 0
        private set

    fun addWord(word: String) {
        str += word
        counter += word.length
    }
}

枚举

enum class Color {
    RED, ORANGE, GREEN
}

enum class ColorValue(private val r: Int, private val g: Int, private val b: Int) {
    RED(255, 0, 0), ORANGE(255, 265, 0), GREEN(0, 255, 0);

    fun rgb() = (r * 256 + g) * 256 + b
}

fun main(args: ArrayList<String>) {
    print(Color.RED)
    print(ColorValue.GREEN)
    print(ColorValue.GREEN.rgb())
}

enum class DeviceStatus(val value: String) {
    POWER("来电"),
    NO_POWER("断电");
}

循环与迭代

fun forFun(start: Int, end: Int) {
    for (i in start..end) {
        print(i)
    }
    for (i in end downTo start step 2) {
        print(i)
    }
    for (i in start until end) {
        print(i)
    }
    //map集合的存取
    val hashMap = HashMap<Char, String>()
    for (c in 'A'..'Z') {
        val binaryString = Integer.toBinaryString(c.toInt())
        hashMap[c] = binaryString
    }
    for ((k, v) in hashMap) {
        print("key = $k, value = $v")
    }
}

@Test
fun testForeach() {
    //continue
    (1..20).forEach {
        println(it)
        if (it >= 10) {
            return@forEach
        }
        println("=$it")
    }
    //break
    run outside@{
        (1..20).forEach inside@{
            println(it)
            if (10 <= it) return@outside
            println("=$it")
        }
    }
}

异常处理

fun verifyNum(str: String) = {
    val num = try {
        Integer.parseInt(str)
    } catch (e: NumberFormatException) {
        null
    }
    print(num)
}

NULL 检查机制

//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1

集合

fun collectionDemo() {
    val arrayListOf = arrayListOf("andy", "tom", "json")
    val hashMapOf = hashMapOf<String, String>("name" to "andy", "grade" to "一年级")
}

函数

定义一个函数

fun <T> joinToString(collection: Collection<T>, sep: String, pre: String, end: String) {
    val sb = StringBuilder(pre)
    for ((index, value) in collection.withIndex()) {
        if (index > 0) {
            sb.append(sep)
        }
        sb.append(value)
    }
    sb.append(end)
    print(sb)
}

命名参数

  • 增加函数的可读性
  • 以想要的顺序指定需要的参数

可以这样调用上面的函数

    val list = listOf(1, 2, 3)
    joinToString(end = "}", pre = "{", sep = ",", collection = list)
    joinToString(list, end = "}", pre = "{", sep = ",")

如果在调用一个函数时,指明了一个参数的名称,那它之后的所有参数都要表明名称。
当调用 Java 函数时,不能使用命名参数。

默认参数

可以避免创建重装函数

fun <T> joinToString(collection: Collection<T>, sep: String = ",", pre: String = "{", end: String = "}") {
    ... ...
}

顶层函数和属性

扩展函数

添加扩展函数

    fun Context.toast(message: CharSequence) = Toast.makeText(this, message, Toast.LENGTH_LONG).show()
    fun String.getLastChar(): Char = this[this.length - 1]
    //在kotlin中使用
    val lastChar = "andy".getLastChar()
    print(lastChar)
    //在java类中使用
    char lastChar = KotlinDemoKt.getLastChar("andy");
  • 扩展函数不存在重写
open class MyView {
    open fun click() {
        println("this is button")
    }
}

class MyButton : MyView() {
    override fun click() {
        println("this is button")
    }
}

fun MyView.showName() = println("my view")
fun MyButton.showName() = println("my button")

fun testDemo() {
    val myButton = MyButton()
    myButton.showName()
    myButton.click()
    val aBtn: MyView = MyButton()
    aBtn.showName()
    aBtn.click()
}
/*
my button
this is button
my view
this is button
*/

扩展属性

//扩展属性
var TextView.leftMargin:Int
get():Int {
     return (layoutParams as ViewGroup.MarginLayoutParams).leftMargin
    }
set(value) {
        (layoutParams as ViewGroup.MarginLayoutParams).leftMargin=value
    }

//================//
var StringBuilder.lastChar: Char
    get() = this[length - 1]
    set(value) = this.setCharAt(length - 1, value)
//   set(value) {
//       this.setCharAt(length - 1, value)
//   }

可变参数

fun varargFun(vararg args: Int) {
    args.forEach {
        println("var =$it")
    }
}

fun testDemo(args: ArrayList<String>) {
    //将参数打包成一个数组,传参数时需要使用*解包
    val ints = intArrayOf(2, 4, 6, 7)
    varargFun(*ints)
}

中缀调用

    val pair0 = 1.to("one")
    val pair1 = 3 to "three"
    val hashMap = HashMap<Int, String>()
    hashMap.plus(pair0)
    hashMap.plus(pair1)

定义中缀调用,需要使用 infix 修饰符来标记

class Student(val name: String, val isMarried: Boolean)

infix fun String.factory(isMarried: Boolean) = Student(this, isMarried)

fun testInFix() {
    val andy: Student = "andy" factory true
    println("name = ${andy.name},isMarried = ${andy.isMarried}")
    println(andy.isMarried)
    val tom = "Tom" factory false
    println("name = ${tom.name},isMarried = ${tom.isMarried}")
}

局部函数

可以在函数中嵌套这些提取的函数,局部函数定义方式和普通函数是相同的

正则表达式

println("23.1|21-6.AB".split("[.|-]".toRegex()))

//第一组(.+)包含最后一个斜线之前的子串
fun parsePath(path: String) {
    val dir = path.substringBeforeLast("/")
    val fileFullName = path.substringAfterLast("/")
    val fileName = fileFullName.substringBefore(".")
    val ext = fileFullName.substringAfter(".")
    println("dir = $dir ,fileName = $fileName ,ext = $ext")
}

fun parsePathByRegex(path: String) {
    var regex = """(.+)/(.+)\.(.+)""".toRegex()
    val entire = regex.matchEntire(path)
    if (entire != null) {
        val (dir, fileName, ext) = entire.destructured
        println("dir = $dir ,fileName = $fileName ,ext = $ext")
    }
}

对象

接口

可以包含默认实现的抽象方法

访问性修饰符

open、final 和 abstract 这三个访问修饰符都 只适用于类,不能用在接口。

  1. open:用于声明一个类可以被继承,或者方法可以被子类重写。
  2. final:不允许类被继承,或者不允许方法被重写。
  3. abstract:声明抽象类,或者抽象类中的抽象方法。
  4. 当我们需要重写方法时,必须加上 override 修饰符。

可见性修饰符

修饰符 类成员 顶层声明
public 所有地方可见 所有地方可见
internal 模块中可见 模块中可见
protected 子类中可见 ---
private 类中可见 文件中可见

内部类和嵌套类

在 Kotlin 中,如果我们像 Java 一样,在一个类的内部定义一个类,那么它并不是一个 内部类,而是 嵌套类,区别在于嵌套类不会持有外部类的引用,也就是说它实际上是一个静态内部类。

class Outer {
    inner class Inner {
        fun getOutReference(): Outer = this@Outer
    }
}

如果要把它嵌套类变成一个 内部类 来持有一个外部类的引用的话需要使用 inner 修饰符,并且访问外部类时,需要使用 this@{外部类名}的方式

密封类 sealed

sealed class Expr {
    class Num(val value: Int) : Expr()
    class Sum(val width: Expr, val height: Expr) : Expr()
}

fun eval(expr: Expr): Int =
        when (expr) {
            is Expr.Num -> expr.value
            is Expr.Sum -> eval(expr.width) + eval(expr.height)
        }

构造方法

主构造方法:主要而简洁的初始化类的方法,并且在 类体外部声明。
从构造方法:在类体内部声明。

class User constructor(name: String) {
    val userName: String

    init {
        userName = name
    }
}

constructor:用来开始一个主构造方法和从构造方法的声明。
init:引入一个初始化块语句,这种语句块包含了在类被创建时执行的代码,并会与主构造方法一起使用,因为主构造方法有语法限制,这就是为什么要使用初始化语句块的原因。

如果主构造方法没有注解或可见性修饰符,可以取消 constructor 关键字。

class User(val name: String)

必须显示地调用父类的构造方法,即使它没有任何的参数

open class User(val name: String)

class TimeUser(name: String) : User(name) {
    fun printUserInfo() = print("user name =${this.name}")
}

从构造方法 constructor

子类调用父类的从构造方法:super
子类调用自己的另一个构造方法:this

数据类

使用 data 修饰符生成通用方法

copy

    val client = Client("tom", 12)
    println(client.toString())
    val copyClient = client.copy("andy")
    println(copyClient)

委托

package cn.timeface

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
 * Created by zhangsheng on 2017/10/28.
 */
class ResourceID {
    val image_id: String = "101"
    val text_id: String = "102"
}

class ResourceLoader(id: ResourceID) {
    private val d: ResourceID = id
    operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, String> {
        if (checkProperty(prop.name)) {
            return DellImpl(d)
        } else {
            throw Exception("Error ${prop.name}")
        }
    }

    private fun checkProperty(name: String): Boolean {
        return name == "image" || name == "text"
    }
}

class DellImpl(d: ResourceID) : ReadOnlyProperty<MyUI, String> {
    val id: ResourceID = d

    override fun getValue(thisRef: MyUI, property: KProperty<*>): String {
        return if (property.name == "image")
            property.name + "  " + id.image_id
        else
            property.name + "  " + id.text_id
    }
}

fun bindResource(id: ResourceID): ResourceLoader {
    return ResourceLoader(id)
}

class MyUI {
    val image by bindResource(ResourceID())
    val text by bindResource(ResourceID())
}

fun main(args: Array<String>) {
    try {
        val ui = MyUI()
        println(ui.image)
        println(ui.text)
    } catch (e: Exception) {
        println(e.message)
    }
}

object

以一句话来定义一个类和一个该类的变量

object Singleton {
    var name: String = "singleton"
    var age: Int = 0
}

fun testSingleTon() {
    println("${Singleton.name}的年龄${Singleton.age}")
}

application 实现单例

class App : Application() {
    companion object {
        lateinit var instance: App //延迟加载,不需要初始化,否则需要在构造函数初始化
            private set
    }
    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}
  1. 创建单例 object

    class LazySingleton private constructor(){
        companion object {
            val instance: LazySingleton by lazy { LazySingleton() }
        }
    }
    
  2. 伴生对象 companion

    伴生对象所在的类被加载,伴生对象被初始化,与 Java 静态成员一样

    工厂方法和静态成员的实现

    使用伴生对象来实现工厂方法

    如果你需要写一个在没有类实例的情况下,调用但是需要访问类内部的函数,可以将其写成那个类中的 对象声明的成员.
    在类中定义的对象之一可以使用一个特殊的关键字来标记 companion,如果这样做,就获得了直接 通过容器类名称来访问这个对象的方法和属性的能力,不再需要显示地指明对象的名称

    class Response(val name: String) {
        companion object Loader {
            fun toJson(response: Response) = "name : ${response.name}"
        }
    }
    
    fun testResponse() {
        Response.Loader.toJson(Response("andy"))
        Response.toJson(Response("Tom"))
    }
    
  3. 在伴生对象中实现接口

    就像其它对象声明一样,伴生对象也可以实现接口,可以将包含它的类的名字当做实现了该接口的对象实例来使用。

    class Response(val name: String) {
        companion object
    }
    
    fun Response.Companion.toJson(response: Response) = "name : ${response.name}"
    fun testResponse() {
        Response.Companion.toJson(Response("andy"))
    }
    
  4. 对象表达式

object 关键字不仅能够用来表明单例式的对象,还能用来声明匿名对象,它替代了 Java 中匿名内部类的用法。

lambda 表达式

        btnLogin.setOnClickListener { v ->
            val id = v.id
        }
        btnLogin.setOnClickListener {

        }

如果 lambda 刚好是 函数或者属性的委托,可以用 成员引用 替换。

data class TimeBook(val name: String, val count: Int)

fun testLambda() {
    val listOf = listOf(TimeBook("andy", 12), TimeBook("tom", 14))
    println(listOf.maxBy { it.count })
    println(listOf.maxBy(TimeBook::count))
}

语法

一个 Lambda 表达式把一小段行为进行编码,你能把它 当做值到处传递,它可以被独立地声明并存储到一个变量中,但是最常见的还是直接声明它并传递给函数

class DiagramTest {
    @Test
    fun testApply() {
        val lam1 = lam("hello", { hello(it) })
        println(lam1)
        val lam2 = lam("hello", {
            it + "world"
        })
        println(lam2)

        val testLam = testLam(10, {
            101
        })
        println(testLam)
    }

    private fun lam(string: String, p: (num: String) -> String): String {
        return p(string)
    }

    private fun hello(string: String): String {
        return string + " world"
    }

    private fun testLam(num: Int, p: () -> Int): Int {
        return p() + num
    }
}

方法参数

    fun functionAbs(x: Int, y: Int, absAction: (Int) -> Int): Int {
        return absAction(x) + absAction(y)
    }

    var absSum: (Int) -> Int = {
        Math.abs(it)
    }

    fun test() {
        functionAbs(1, -2, absSum)
    }

存储到变量中

    val sum = { x: Int, y: Int -> x + y }
    println(sum(1, 3))

直接调用 run

fun main(args : ArrayList<String>){
    run {
        println("hello world")
    }
}

joinToString

    val toString = listOf.joinToString("==", "[", "]",
            transform = { timeBook: TimeBook -> timeBook.name })
    println(toString)

    @Test
    fun testKotlin() {
        val listStr = (1..5).joinToString(",") { "$it:$it" }
        println(listStr)
        //1:1,2:2,3:3,4:4,5:5
        val list = arrayListOf<String>()
        list.add("8")
        list.add("9")
        val str = list.joinToString(",") { "${list.indexOf(it)}:$it" }
        println(str)
        //0:8,1:9
        val result = listStr
                    .split(",".toRegex())
                    .map { it.split(":")[1] }
                    .filter { !it.isEmpty() }
        println(result)
        //1,2,3,4,5
    }

包含多条语句

lambda 表达式可以包含更多的语句,最后一个表达式就是 lambda 的结果

    val total = { x: Int, y: Int ->
        println("start count")
        x + y
        }
    println(total(1, 4))
    bookList.forEach {
        print("${it.name} has ${it.count}")
    }

引用顶层函数

直接以:开头,成员引用::salute 被当作实参传递给库函数 run

fun salute() = print("hello")
fun main1(args: ArrayList<String>) {
    run(::salute)
}

存储或者延期执行创建类实例的动作

    val createBook = ::TimeBook
    println(createBook("tommao", 12))

引用扩展函数

fun TimeBook.hasMore() = count > 0

fun main1(args: ArrayList<String>){
    bookList.filter { it.count > 0 }
    val hasNext = TimeBook::hasMore
    bookList.filter(hasNext)
    println(list)
}

also\run\apply\with\let

also\run\apply\with\let

collections

list to map

 val myMap = myList.map { it.name to it.age }.toMap()
 val myMap = myList.associateBy({ it.name }, { it.hobbies })
 val myMap = myList.associate{ it to it }
//1.
 val myMap = mutableMapOf<String, String>()
 myList.associateTo(myMap) {it to it}
//2.
val myMap = mutableMapOf<String, User>()
myList.associateByTo(myMap) {it.name}
//3.
val myMap = mutableMapOf<String, Int>()
myList.associateByTo(myMap, {it.name}, {it.age})

** 知识索引 **
kotlin 语言中文站

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容