Learn Kotlin

Kotlin优势

  • 空安全 :在编译时期处理各种null情况,避免执行时异常。
  • 函数式支持:它使用了很多函数式编程概念。
  • 扩展函数:可以给任何类添加扩展函数。
  • 高度互操作性:Kotlin和Java两个语言之间可以互操作,两种语言互相调用,混合编程。

构造函数

在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次级构造函数。主构造函数是类头的一部分:它跟在类名后。

//firstName可以在initializer blocks中使用
class Person constructor(firstName: String) {
}

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor关键字。

class Person(firstName: String) {
}

主构造函数不能包含任何的代码。
初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中:

class Person(name: String) {
    init {
        CLog.d("Person initialized with name ${name}")
    }
}

注意,主构造的参数可以在初始化块中使用。它们也可以在类体内中声明属性时使用:

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

事实上,声明属性以及从主构造函数初始化属性,Kotlin 有简洁的语法,与普通属性一样,主构造函数中声明的属性可以是可变的(var)或只读的(val):

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

如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前面:

class Person private constructor(name: String) {
    //......
}

次级构造函数

类也可以声明前缀有 constructor的次构造函数。
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可。
如果你没有委托主构造函数,系统会提示:Primary constructor call expected

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类有一个公有构造函数,你需要声明一个带有非默认可见的空的主构造函数:

class DontCreateMe private constructor () {
}

继承

在 Kotlin 中所有类都有一个共同的超类 Any,它是没有继承父类的类的默认超类:

class Example // 从 Any 隐式继承

默认每个类在创建的时候都是final的,如果没有添加open关键字,你在继承的时候系统会提示This type is final, so it cannot be inherited from

要声明一个显式的父类,我们把类型放到类头的冒号之后:

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

如果该父类有一个主构造函数,则子类可以(并且必须) 用父类型的)主构造函数参数就地初始化。如果没有执行这一步,系统会提示This type has a constructor , and thus must be initailized here

如果类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其父类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用父类型的不同的构造函数:

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

如果你不执行这一步,系统会提示你Explicit "this" or ''super" call is required. There is no constructor in superclass that can be called without arguments
类上的open 标注与 Java 中 final 相反,它允许其他类从这个类继承。
默认情况下,在 Kotlin 中所有的类都是 final。
要么为继承而设计,并提供文档说明,要么就禁止继承。

伴生对象

在 Kotlin 中类没有static的方法和变量,所以需要使用Companion Object来声明类似static的方法和变量。
其实这些变量并不是真正的static变量,而是一个伴生对象,这个伴生对象位于类中定义的一个叫做Companion的内部类中。
类内部的对象声明可以用 companion 关键字标记:

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

该伴生对象的成员可通过只使用类名作为限定符来调用:

val instance = MyClass.create()

在Java中如何调用Companion Object的属性和方法呢?

MyClass.INSTANCE.create()

当然,在 JVM 平台,如果使用 @JvmStatic @JvmField注解,你可以将伴生对象的成员生成为真正的
静态方法和字段。

我们来看一下Companion Object的具体应用场景:


CapaCameraActivity.png

字节码:


字节码.png

Decompile To Java:


Decompile To Java.png

Getter And Setter

首先来看在Kotlin中如何声明一个属性,属性可以用关键字var 声明为可变的,否则使用只读关键字val

class Address {
    var name: String = ……
    var street: String = ……
    var city: String = ……
    var state: String? = ……
    var zipCode: String = ……
}

其实,声明一个属性的完整语法是:

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

其中,initializer、setter、getter都是可选的。属性的类型如果可以从initializer或getter中推断出来,也可以省略。
其中,一个只读属性(val)和一个可变属性(var)的区别是:只读属性不允许setter。

一个自定义gettter的例子:

val isEmpty: Boolean
    get() = this.size == 0

一个自定义setter的例子:

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // 解析字符串并赋值给其他属性
    }

如果你需要改变一个访问器的可见性或者对其注解,但是不需要改变默认的实现, 你可以定义访问器而不定义其实现:

var setterVisibility: String = "abc"
    private set // 此 setter 是私有的并且有默认实现

var setterWithAnnotation: Any? = null
    @Inject set // 用 Inject 注解此 setter

具体使用场景:需要自定义getter和setter的方法举例:

扩展函数

扩展函数数是指在一个类上增加一种新的行为,甚至我们没有这个类代码的访问权限。
Koltin可以对一个类的属性和方法进行扩展,它是一种静态行为,对扩展的类代码不会造成任何影响。
扩展函数的定义形式:

fun receiverType.functionName(params){
    body
}
  • receiverType:表示函数的接收者,也就是函数扩展的对象
  • . :扩展函数的修饰符
  • functionName:扩展函数的名称
  • params:扩展函数的参数,可以为NULL

一个简单的扩展函数的举例:

//声明一个User类
class User(var name:String)

//定义一个简单的打印User名字的扩展函数
fun User.Print(){
    print("用户名 $name")
}

fun main(arg:Array<String>){
    var user = User("Runoob")
    user.Print()
}

项目中已有的最简便的扩展函数的应用(ViewExtensions):

package com.xingin.common

import android.text.SpannableString
import android.view.View
import android.widget.TextView

/**
 * Created by chris on 03/08/2017.
 */
fun View.hide() {
    this.visibility = View.GONE
}
fun View.show() {
    this.visibility = View.VISIBLE
}
fun View.showIf(shouldShow: Boolean) {
    this.visibility = if (shouldShow) View.VISIBLE else View.GONE
}
fun View.hideIf(shouldHide: Boolean) {
    showIf(!shouldHide)
}

fun TextView.setTextOrHide(text: CharSequence) {
    this.text = text
    showIf(text.isNotEmpty())
}

import java.io.File

/**
 * Created by Kathy on 2017/9/25.
 */
fun File.mkdirIfNotExists() {
    if (!exists()) mkdirs()
}

fun File.deleteIfIsFile() {
    if (isFile && exists()) delete()
}
public final class FileExtensionsKt {
   public static final void mkdirIfNotExists(@NotNull File $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      if(!$receiver.exists()) {
         $receiver.mkdirs();
      }

   }

   public static final void deleteIfIsFile(@NotNull File $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      if($receiver.isFile() && $receiver.exists()) {
         $receiver.delete();
      }

   }

Kotlin扩展函数允许我们在不改变已有类的情况下,为类添加新的函数。
最常用的扩展函数的实例:

fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}

然后在我们的Activity中将它作为普通方法来直接使用:

override fun onCreate(savedInstanceState: Bundle?) { 
    toast("This is onCreate!!")
    toast("Hello world!", Toast.LENGTH_LONG)
    toast(message = "Hello world!", duration = Toast.LENGTH_LONG)
}

End

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

推荐阅读更多精彩内容