Kotlin 实战笔记
使用 interface
关键字来声明一个接口。可以包含抽象方法和非抽象方法(带默认实现的方法):
interface Clickable { // 抽象方法 fun click() // 非抽象方法(带默认实现的方法),子类可以不重写 fun showOff() = println("I`m clickable!") }
实现类可以使用类似 Java 的 super
关键字来调用接口的非抽象方法;但是,如果实现的多个接口中有相同名字的非抽象方法,此时实现类必须重写该方法,并且对它们的调用也有所不同:
interface Focusable { // 两个非抽象方法 fun setFocus(b: Boolean) = println("I ${if (b) "got" else "lost"} focus") fun showOff() = println("I`m focusable!") } class Button : Clickable, Focusable { override fun click() { // 调用 Focusable 接口的 setFocus() 方法 super.setFocus(true) } // 此时必须重写该方法 override fun showOff() { // 调用 Clickable 接口的 showOff() 方法 super<Clickable>.showOff() // 调用 Focusable 接口的 showOff() 方法 super<Focusable>.showOff() } }
Kotlin 中默认情况下类、方法、属性都是是 final
和 public
的,即不可被继承(不可重写),需要显式的用 open
关键字修饰才可被继承(可重写):
open class Button : Clickable, Focusable { // 子类不可重写 val enable: Boolean = true // 子类可重写 open val text: String = "Button" // 子类不可重写 fun disable() {} // 子类可重写 open fun animate() {} ... } // 子类可通过关键字 super 调用父类非 private 的属性和方法 class RichButton : Button() { // 重写子类属性 override val text: String get() = super.text + super.enable // 重写父类方法 override fun animate() { super.animate() super.disable() } }
使用 abstract
关键字来声明抽象类,抽象方法默认是 open
的,而非抽象方法并不是默认为 open
的:
abstract class Animated { // 抽象方法默认 open abstract fun animate() // 非抽象方法可以使用 open,否则不能被重写 open fun stopAnimating() {} }
嵌套类类似于 Java 中的静态内部类,不持有外部类的引用;内部类类似于 Java 中的普通内部类,持有外部类引用:
class Outer { val test: Int = 0 // 嵌套类,类似于 Java 的静态内部类,不持有外部类引用 class nest {} // 内部类,持有外部类引用 inner class Inner { // 使用 this@外部类名字(比如 this@Outer)访问外部类 fun test() = this@Outer.test } }
constructor
关键字用来构造方法的声明; init
关键字用来引入初始化语句块,类似于 Java 构造方法中的初始化工作。
在声明类时用括号包裹起来的就是主构造方法,它的目的有两个:表明构造方法的参数、声明属性并将参数赋值给属性。
// (val nickname: String) 就是主构造方法 // 表明了构造方法参数、声明了一个 nickname 的属性并将参数赋值给属性 class User(val nickname: String) // 如果主构造方法中没有注解或可见性修饰符,可以省略 constructor class User constructor(nickname: String) { val nickname: String // 初始化语句块 init { this.nickname = nickname; } }
上面两个 User 的声明是等价的。构造方法参数可以和普通方法参数一样使用默认值。
如果想要确保类不被其他代码实例化,需要使用 private
关键字修饰构造方法:
class Secretive private constructor() {}
除了主构造方法外还可以声明多个从构造方法:
class View { // 从构造方法 constructor(ctx: Context) {} constructor(ctx: Context, attr: AttributeSet) } class Button: View { // 调用自己的另一个构造方法 constructor(ctx: Context): this(ctx, null) // 调用父类构造方法 constructor(ctx: Context, attr: AttributeSet): super(ctx, attr) }
使用 data
关键字可以声明类为数据类。
data class Client(val name: String, val postalCode: Int)
数据类自动生成了一些常用的方法:
object
关键字定义一个类并同时创建一个实例(对象),使用场景:
一个对象声明可以包含属性、方法、初始化语句块,但是不允许声明构造方法(包括主构造方法和从构造方法),同时也可以继承类和接口。
// object 将类的声明和对象的创建放在了一起 // 内部是通过静态代码块创建了一个对象 object Singleton { val list= arrayListOf(1, 2, 3) fun singleton() { for (i in list) { print(i) } } } fun main(args: Array<String>) { Singleton.list.add(4) Singleton.singleton() }
对象声明也可以在类中,这样的对象同样只有一个单一实例:
class Test { private val test = 0 object Singleton { val list= arrayListOf(1, 2, 3) fun singleton() { // 可以访问外部类 private 成员 println(Test().test) for (i in list) { print(i) } } } } fun main(args: Array<String>) { Test.Singleton.list.add(4) Test.Singleton.singleton() }
看看编译后的 Java 代码:
public static final class Singleton { @NotNull private static final ArrayList list; public static final Test.Singleton INSTANCE; @NotNull public final ArrayList getList() { return list; } public final void singleton() { Iterator var2 = list.iterator(); while(var2.hasNext()) { Integer i = (Integer)var2.next(); Intrinsics.checkExpressionValueIsNotNull(i, "i"); int var3 = i; System.out.print(var3); } } static { Test.Singleton var0 = new Test.Singleton(); INSTANCE = var0; list = CollectionsKt.arrayListOf(new Integer[]{1, 2, 3}); } }
伴生对象就是在对象声明的基础上使用了 companion
关键字,但是伴生对象必须声明在类里面。
class Factory { private val abc = 0 private fun aaa() {} // Test 可以省略 companion object Test { fun p() { // 可以访问外部类的 private 成员 Factory().abc Factory().aaa() } } } fun main(args: Array<String>) { Factory.p() }
伴生对象和对象声明很像,但是它们编译后的 Java 代码却有区别:
public final class Factory { private final int abc; public static final Factory.Test Test = new Factory.Test((DefaultConstructorMarker)null); private final void aaa() {} public static final class Test { public final void p() { (new Factory()).abc; (new Factory()).aaa(); } private Test() {} public Test(DefaultConstructorMarker $constructor_marker) { this(); } } }
匿名对象替代了 Java 中的匿名内部类的用法:
// Java button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "onClick: "); } }); // Kotlin button.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { Log.d(TAG, "onClick") } })