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的具体应用场景:
字节码:
Decompile To Java:
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