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")
}
})