1. 可见性
- private 只在该类(以及它的成员)中可见
- protected 和 private 一样但在子类中也可见
- internal 在本模块的所有可以访问到声明区域的均可以访问该类的所有 internal 成员
- public 任何地方可见 当没写明可见性,默认为public
- 特别的:外部类不可以访问内部类的 private 成员。
以上模块是指
- an IntelliJ IDEA module;
- a Maven or Gradle project;
2. 构造函数
类可以包含一个主构造函数和多个二级构造函数,完整声明如下。如果构造函数不包括可见性声明和注解,则constructor可以省略
class Customer public @inject constructor (name: String) {...}
主构造函数不能包含任意代码,初始化代码放在init{}代码块内
主构造函数可以直接初始化域变量,也可以(必须)直接带入超类的参数中
class Person(val firstName: String, val lastName: String, var age: Int) : BasePerson(firstName, lastName) {
}
二级构造函数需要constructor前缀
如果存在主构造函数,每一个二级构造函数都要直接调用,或间接通过另一个二级构造函数代理调用主构造函数,用this关键字来指代其他构造函数
class Person(val name: String) {
constructor (name: String, paret: Person) : this(name) {
parent.children.add(this)
}
}
构造函数中的参数可以有默认值,类似于python,当有默认值的参数,new的时候,可以忽略而使用默认值
实际上Kotlin创建对象不使用new 直接使用 Person("Wang")
3. Kotlin的接口
Kotlin的接口允许包含了方法的实现,但和抽象类不同的是,接口不能保存状态。如下,
- C实现A接口,bar()方法是抽象的,则强制需要重写实现它。
- D同时实现A和B,对于bar()方法,因为B已经包含了实现,所以会默认选择B的实现;但因为A和B都包含了对foo()这个方法的实现,造成了冲突,所以强制重写此方法。
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
4. 类继承和方法属性复写
- kotlin 中坚持做明确的事,Kotlin的类默认下都是final的,需要加上open关键字才可被继承。而抽象类和接口则天生是open的。
- 对于可以复写的方法,Kotlin也需要明确使用open来声明,并且重写他们
- override的成员也是open的,如果想终结这个open,需要加final
- 可以用var去覆盖一个val,但反之则不允许
- 当继承的类和实现的接口包含同名同参方法,并且均有实现时,需要重新复写这个方法(参考接口)
5. 属性
属性可以有get set方法
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
备份域:
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0)
field = value
}
备份属性
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null)
_table = HashMap() // Type parameters are inferred
return _table ?: throw AssertionError("Set to null by another thread")
}
对于成员属性,不管是空或非空,都不许再定义的时候初始化。
如果想延迟初始化,有以下方法:
val a : Any by Delegates.notNull()
var b : Any by Delegates.notNull()
lateinit var c : Any
val d : Any? by lazy { }
val e : Any by lazy { }
6. 枚举类
- 实际上每一个枚举都是一个对象实例。
- 此外,枚举类实现了Comparable接口,比较适使用的是枚举类的自然顺序进行比较
// 1 普通枚举类
enum class Direction {
NORTH,SOUTH,WEST,EAST
}
// 2 可初始化的枚举类
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
// 3 可以声明枚举实例自己的匿名类
enum class SwitchState {
ON {
override fun switch() = OFF
},
OFF{
override fun switch() = ON
};
abstract fun switch(): SwitchState
}
7. 密封类 sealed
- 密封类相当于一个枚举类的扩展:枚举值集合的类型是严格限制的,但每个枚举常量只有一个实例,而密封类的子类可以有包含不同状态的多个实例。
- 密封类主要的好处体现在when表达式,可以确保声明覆盖所有情形而不需要else
sealed class Expr {
class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// the `else` clause is not required because we've covered all the cases
}
8. 代理属性
有些常用属性,其读写方法可以进行抽象(类似于Gson的属性封装),get set方法可以通过代理类进行封装,以后可以重用该代理。
class Delegate {
fun get(thisRef: Any?, prop: PropertyMetadata): String {
return "$thisRef, thank you for delegating '${prop.name}' to me !"
}
fun set(thisRef: Any?, prop: PropertyMatada, value: String) {
println("$value has been assigned to '${prop.name} in $thisRef.'")
}
}
class Example {
var p: String by Delegate()
}
Kotlin提供的标准代理
kotlin.properties.Delegates 对象是标准库提供的一个工厂方法并提供了很多有用的代理
// 懒加载
val lazy: String by Delegates.lazy { // 如果你想要线程安全,使用 blockingLazy()
println("computed!")
"Hello"
}
// 观察者
var name: String by Delegates.observable("default") {
d,old,new -> println("$old -> $new")
}
// NotNull
// 正常来说 非空的var不允许不初始化,也不允许用null初始化
class Foo {
var bar: Bat //错误必须初始化
}
// 使用Delegates.notNull()可以作为替代进行null初始化,但如果在set之前get,则会抛出异常
class Foo {
var bar: Bar by Delegates.notNull()
}
// 在Map中解析属性
class User(val map: Map<String, Any?>) {
val name: String by Delegates.mapVal(map) // var 则可以用 mapVar
val age: Int by Delegates.mapVal(map)
}
9. 代理类
Kotlin提供代理类的方式去快速实现代理模式
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { printz(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
10. 嵌套类
嵌套类可分为
- 一般嵌套类:这个跟Java差不多,不展开讲。区别是无法引用外部类的属性和方法
- 内部类:用inner修饰,可以引用外部类的属性和方法。但外部类无法使用内部类的private属性或方法
- 匿名内部类:使用对象表达式创建,参考下一条“对象声明”
class Outer {
private val bar: Int = 1
// 内部类
inner class Inner {
fun foo() = bar
}
}
11. 对象声明
Kotlin中有3种可以进行声明的对象
- 匿名内部类
- 单例对象
- 伴随对象
// 类似于匿名内部类
button.setOnClickListener( object : OnClickListener {
override fun onClick(v: View) {
// do something
}
})
// 单例
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
}
// 伴随对象 其中声明的方法和属性相当于是java的类里面声明的静态属性和静态方法,可以使用类名来直接引用
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
以上单例类似于java中的
public final class DataProviderManager {
private DataProviderManager() {}
public static DataProviderManager INSTANCE = new DataProviderManager();
void registerDataProvider(DataProvider provider) {
// ...
}
}
12. 多重声明与data类
针对常用的数据存储类Java bean,Kotlin提供了更便捷的data类。data类的限制如下
- 主构造函数至少有一个参数
- 所有构造函数参数必须标注为val或var,即不能是局部变量
- 数据类不能使abstract open sealed 或 inner
- 不能继承,但能实现接口
- 如果在 JVM 中如果构造函数是无参的,则所有的属性必须有默认的值
- data class User(val name: String = "", val age: Int = 0)
data class User(val name: String, val age: Int)
// toString() of the form "User(name=John, age=42)";
// copy() 便于原型模式的使用
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
// componentN() 便于多重声明和多重返回,对应构造参数的声明顺序
val (name, age) = person
val name = persion.component1()
val age = persion.component2()
data class Result(val result: Int, val status: Status)
fun function(...): Result {
//...
return Result(result, status)
}
val (result, status) = function(...)
- Kotlin标准库提供了 Pair 和 Triple 两种标准data类,但可读性不强不建议使用。其实可以作为多参数返回时使用。
13. 类的扩展
在 java 中,我们通常使用一系列名字为 "Utils" 的类: FileUtils,StringUtils等等。很有名的 java.util.Collections 也是其中一员的,但我们不得不像下面这样使用他们:
Collections.swap(list,
Collections.binarySearch(list, Collections.max(otherList)),
Collections.max(list));
Kotlin类的扩展可以让我们可以使用以下方式去引用这样的工具类静态方法
list.swap(list.binarySearch(otherList.max()), list.max())
如对MutableList<T>使用类的扩展:
fun <T> MutableList<T>.swap(x: Int, y: Int) {
val temp = this[x] // this 对应 list
this[x] = this[y]
this[y] = tmp
}
类的扩展需要注意以下几点:
- 类的扩展是被静态解析的,如果有同名同参数的成员函数和扩展函数,调用的时候必然会使用成员函数
- 被扩展的类甚至可以为Any?
- 可扩展的包括 方法、属性、伴随对象
- 扩展的作用域 仅当前包有效。其他包要使用某个扩展需要进行引用,如以上扩展,需要执行如下导入:
import x.x.x.swap