转载

《Kotlin 实战》第4章笔记

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

final、open

Kotlin 中默认情况下类、方法、属性都是是 finalpublic 的,即不可被继承(不可重写),需要显式的用 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

使用 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)

数据类自动生成了一些常用的方法:

  • equals:用来比较实例
  • hashCode:用来作为例如 HashMap 这种基于哈希容器的键
  • toString:用来为类生成按声明顺序排列的所有字段的字符串表达形式
  • copy:用来创建对象副本

object 关键字

object 关键字定义一个类并同时创建一个实例(对象),使用场景:

  • 对象声明:是定义单例的一种方式
  • 伴生对象:可以持有工厂方法和其他与这个类相关
  • 对象表达式:用来替代 Java 的匿名内部类

对象声明:单例

一个对象声明可以包含属性、方法、初始化语句块,但是不允许声明构造方法(包括主构造方法和从构造方法),同时也可以继承类和接口。

// 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")
    }
})
原文  https://ljuns.itscoder.com/2018/10/31/《Kotlin-实战》第4章笔记/
正文到此结束
Loading...