博客主页java
kotlin的接口能够包含抽象方法的定义和非抽象方法的实现,但不能包含任何状态。json
// 使用interface关键字声明一个kotlin接口 interface Clickable { fun click() } // kotlin 在类名后面使用冒号(:)代替java中的extends和implements关键字 // 与java同样,一个类能够实现任意多个接口,但只能继承一个类 class Button : Clickable { // override 修饰符用来标注被重写的父类或者接口的方法和属性,与java中的@Override注解相似 // kotlin中override 修饰符是强制要求的 override fun click() { println(" i was clicked.") } }
接口中的方法能够有一个默认实现,只须要提供一个方法体。segmentfault
interface Clickable { fun click() // 带默认实现的方法,在子类中也能够从新定义该方法的行为 fun showOff() = println("I'm clickable.") }
若是在类中同时实现了两个接口,而这两个接口都包含了带默认实现的showOff方法,若是没有显式实现showOff,编译器报错,强制要求提供实现。ide
interface Focusable { fun showOff() = println("I'm focusable.") fun setFocus(b: Boolean) = println("i ${if (b) "got" else "lost"}") } class Button : Clickable, Focusable { // 若是实现的两个接口中有相同的默认的实现方法,必须显式的实现一个 override fun showOff() { // 使用尖括号加上父类型名字的super代表了想要调用哪个父类的方法 // java中把基类的名字放在super关键字的前面:Clickable.super.showOff() super<Clickable>.showOff() super<Focusable>.showOff() } override fun click() { println(" i was clicked.") } }
java的类和方法默认是open的,而kotlin中默认都是final的。函数
// 声明一个带一个open方法的open类,其余的类能够继承它 open class RichButton : Clickable { // 这个函数重写一个open函数且它自己一样是open的 // public void click() override fun click() {} // 这个函数时final的,不能在子类中重写它 // public final void disable() fun disable() {} // 这个函数是open的,能够在子类中重写它,若是不想子类重写它,能够显式的标注为final // public void animate() open fun animate() {} }
kotlin中声明一个抽象类使用 abstract 修饰符,这种类不能实例化。post
abstract class Animated { // 这个函数时抽象的,它没有实现必须被子类重写。抽象成员始终是open的 // public abstract void animate(); abstract fun animate() // 抽象类中的非抽象函数并非默认open的,但能够标注为open的 // public void stopAnimating() open fun stopAnimating() {} // 抽象类中的非抽象函数并非默认open的 // public final void animateTwice() fun animateTwice() {} }
类中访问修饰符的意义,在接口中,不能使用final、open或者abstract,接口中的成员始终是open的,不能将其声明为final的。this
修饰符 | 相关成员 | 评注 |
---|---|---|
final | 不能被重写 | 类中成员默认使用 |
open | 能够被重写 | 须要明确的代表 |
abstract | 必须被重写 | 只能在抽象类中使用,抽象成员不能有实现 |
override | 重写父类或者接口中的成员 | 若是没有使用final代表,重写的成员默认是开放的 |
kotlin中的可见性修饰符与java相似,public、private、protected,但默认的可见性不同,kotlin中若是省略了修饰符,声明就是public的,java中默承认见性是包私有,kotlin中并无使用,kotlin只把包做为在命名空间里组织代码的一种方式使用,没有将其做可见性控制。rest
kotlin提供一个新的修饰符,internal,表示 只在模块内部可见。internal 优点在于它提供了对模块实现细节的真正封装。java中外部代码能够将类定义到与你代码相同的包中,从而获得访问包私有声明的权限。code
kotlin也容许在顶层声明中使用private可见性,包括类、函数、属性。这些声明就会只在声明它们的文件中可见。对象
修饰符 | 类成员 | 顶层声明 |
---|---|---|
public(默认) | 全部地方可见 | 全部地方可见 |
internal | 模块内部可见 | 模块内部可见 |
protected | 子类中可见 | --- |
private | 类中可见 | 文件中可见 |
kotlin的嵌套类不能访问外部类的实例。在java中,在另外一个类中声明一个类时,它会默认变成内部类,隐式地存储了它的外部类的引用,若是内部类上加上static修饰符,静态的嵌套类会从这个类中删除包围它的类的隐式引用。
interface State : Serializable interface View { fun restoreState(state: State) fun getState(): State } class Button : View { override fun getState(): State { return ButtonState() } override fun restoreState(state: State) { } // kotlin中没有显式修饰符的嵌套类与java中的static嵌套类同样 // 若是要把它变成一个内部类来持有一个外部类的应用,须要使用inner修饰符 class ButtonState : State {} }
嵌套类和内部类在java与kotlin中对应关系
类A在另外一个类B中声明 | 在java中 | 在kotlin中 |
---|---|---|
嵌套类(不存储外部类的引用) | static class A | class A |
内部类(存储外部类的引用) | class A | inner class A |
在kotlin中,引用外部类实例的语法与java不一样,须要使用 this@Outer 从Inner类去访问Outer类
class Outer { inner class Inner { fun getOuterReference() : Outer = this@Outer } }
先来看下做为接口实现的表达式,必须检查else分支
interface Expr class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : Expr fun eval(e: Expr): Int = when (e) { is Num -> e.value is Sum -> eval(e.left) + eval(e.right) // 必须检查else分支 else -> throw IllegalArgumentException("Unknown") }
kotlin提供了一个解决方案:sealed类。为父类添加一个sealed修饰符,对可能建立的子类作出严格的限制,全部直接子类必须嵌套在父类中。
// 将基类标记为密封类 // sealed修饰符隐含这个类是一个open类,不需显式的添加open修饰符 sealed class Expr { // 将全部可能的类做为嵌套类列出 class Num(val value: Int) : Expr() class Sum(val left: Expr, val right: Expr) : Expr() } // when表达式处理全部的sealed类的子类,不在须要提供默认的else分支 fun eval(e: Expr): Int = when (e) { is Expr.Num -> e.value is Expr.Sum -> eval(e.left) + eval(e.right) }
// 这段被括号围起来的语句块叫做主构造方法,代表构造方法的参数,以及定义使用这些参数初始化的属性 // val 意味着相应的属性会用构造方法的参数来初始化 class User(val nickname: String) // constructor 关键字用来开始一个主构造方法或者从构造方法的声明 class User constructor(_nickname: String) { val nickname: String // init关键字用来引入一个初始化语句块,包含了在类被建立时执行的代码,与主构造方法一块儿使用 // 能够在一个类中声明多个初始化语句块 init { nickname = _nickname } } // 若是主构造方法没有注解或者可见性修饰符,能够去掉constructor关键字 class User(_nickname: String) { val nickname = _nickname }
能够像函数参数同样为构造方法参数声明一个默认值
// 为构造方法参数提供一个默认值 class User( val _nickname: String, val isSubscribed: Boolean = true ) val kerwin = User("kerwin", false) println(kerwin.isSubscribed) // false val blob = User("blob") // isSubscribed参数使用默认值true println(blob.isSubscribed) // true // 能够显式的为某些构造方法参数标明名称 val alice = User("Alice", isSubscribed = false) println(alice.isSubscribed) // false
若是一个类有父类,且该类的主构造方法也须要初始化父类,能够经过该类的主构造方法参数引用提供给父类构造方法参数
open class User( val nickname: String, val isSubscribed: Boolean = true ) class TwitterUser(nickname: String) : User(nickname)
若是类不被其余代码实例化,必须把构造方法标记为private
// 由于Person类只有一个private的构造方法,这个类外部代码不能实例化它 class Person private constructor() {}
// 这个类没有主构造方法,声明了两个从构造方法 // 从构造方法能够声明多个 open class View { constructor(ctx: Context) {} constructor(ctx: Context, attr: AttributeSet?) {} } class MyButton : View { // 可使用this关键字,调用本身类的另外一个构造方法 constructor(ctx: Context) : this(ctx, null) {} // 调用父类构造方法,使用super关键字调用父类对应的构造方法 constructor(ctx: Context, attr: AttributeSet?) : super(ctx, attr) {} }
kotlin中,接口能够包含抽象属性声明。
// 接口没有说明这个属性存储到一个支持字段仍是经过getter来获取,自己并不包含任何状态 interface User { val nickname: String } // 直接在主构造方法声明一个来自实现了User抽象属性 class PrivateUser(override val nickname: String) : User // 经过自定义getter,每次访问须要计算 class SubscribingUser(val email: String) : User { override val nickname: String get() = email.substringBefore("@") } // 属性初始化,一个支持字段来存储 class FacebookUser(val accountId: Int) : User { override val nickname = accountId.toString() }
接口还能够包含具备getter和setter属性,只要它们没有引用一个支持字段
interface User2 { // 必须在子类中重写 val email: String // 能够被继承,属性没有支持字段,结果值在每次访问时经过计算获得 val nickname: String get() = email.substringBefore("@") }
// 在setter中访问支持字段 class User(val name: String) { var address: String = "Unknown" set(value) { println("Addressn was changed for $name: $field -> $value") // 使用特殊的标识符field 来访问支持字段的值 // getter中只能读取值,setter中,既能读取它也能修改它 field = value } } // 修改属性的值,在底层调用了setter >>> val user = User("kerwin") >>> user.address = "shanghai"
访问器的可见性默认与属性的可见性相同。
// 声明一个具备private setter的属性 class LengthCounter { // 不能在类外部修改这个属性,确保只能在类中被修改 var counter: Int = 0 private set fun addWord(word: String) { counter += word.length } }
字符串表示:toString()
class Client( val name: String, val postalCode: Int ) { // 重写toString override fun toString() = "Client{name:$name, postalCode:$postalCode}" } >>> val client = Client("kerwin", 1234) >>> println(client) Client{name:kerwin, postalCode:1234}
对象相等性:equals()
class Client( val name: String, val postalCode: Int ) { override fun toString() = "Client{name:$name, postalCode:$postalCode}" // 重写equals override fun equals(other: Any?): Boolean { // kotlin中is检查一个值是否为一个指定的类型 if (other == null || other !is Client) { return false } return name == other.name && postalCode == other.postalCode } } // 在kotlin中,==检查对象是否相等,而不是比较引用 >>> val client1 = Client("kerwin", 1234) >>> val client2 = Client("kerwin", 1234) >>> println(client1 == client2) true
在kotlin中,==运算符是比较两个对象的默认方式:本质上就是经过调用equals来比较两个值的。若是进行引用比较,可使用 ===运算符
Hash容器:hashCode()
hashCod方法一般与equals一块儿被重写。
// 什么状况?返回了false // 缘由就是Client缺乏了hashCode方法,若是两个对象相等,必须有这相同的hash值 // HashSet中的值首先比较它们的hash值,相等才会比较值 >>> val set = hashSetOf(Client("kerwin", 1234)) >>> println(set.contains(Client("kerwin", 1234))) false class Client( val name: String, val postalCode: Int ) { // ... // 重写hashCode override fun hashCode(): Int = name.hashCode() * 31 + postalCode }
在kotlin中,在类添加data修饰符,自动生成通用的方法(toString、equals、hashCode)实现。
// 数据类Client // equals和hashCode方法会将全部在主构造方法中的声明的属性归入考虑。 // 注意:没有在主构造方法中声明的属性将不会加入到相等性检查和哈希值计算中去 data class Client( val name: String, val postalCode: Int )
数据类和不可变性:copy()方法
数据类的属性推荐只使用只读属性(val),让数据类的实例不可变。
为了让使用不可变对象的数据类变得更容易,kotlin编译器为它们多生成了一个方法,一个容许copy类的实例的方法,并在copy的同时修改某些属性的值。
class Client( val name: String, val postalCode: Int ) { override fun toString() = "Client{name:$name, postalCode:$postalCode}" fun copy( name: String = this.name, postalCode: Int = this.postalCode ) = Client(name, postalCode) } >>> val client = Client("kerwin", 1234) >>> println(client.copy(postalCode = 4321)) >>> println(client) Client{name:kerwin, postalCode:4321} Client{name:kerwin, postalCode:1234}
class DelegatingCollection<T> : Collection<T> { private val innerList = arrayListOf<T>() override val size: Int get() = innerList.size override fun contains(element: T): Boolean = innerList.contains(element) override fun containsAll(elements: Collection<T>): Boolean = innerList.containsAll(elements) override fun isEmpty(): Boolean = innerList.isEmpty() override fun iterator(): Iterator<T> = innerList.iterator() }
在kotlin中,不管何时实现一个接口,均可以使用by关键字将接口的实现委托到另外一个对象。
// Collection接口的实现经过by关键字委托给innerList // 编译器会自动生成方法的实现 class DelegatingCollection<T>( val innerList: Collection<T> = arrayListOf() ) : Collection<T> by innerList
对象声明是经过 object 关键字引入。
// 与类同样,一个对象声明也能够包括属性、方法、初始化语句块等。 // 惟一不容许的有构造方法(包括主构造方法和从构造方法) // 与普通类的实例不一样,对象声明在定义的时就当即建立 object Payroll { val allEmployees = arrayListOf<Person>() fun calculateSalary() { for (person in allEmployees) { } } } // kotlin中的对象声明被编译成了经过静态字段来持有它的单一实例的类,这个字段名字始终都是INSTANCE // kotlin代码转换为java代码 public final class Payroll { @NotNull private static final ArrayList allEmployees; public static final Payroll INSTANCE; @NotNull public final ArrayList getAllEmployees() { return allEmployees; } public final void calculateSalary() { Person var1; for(Iterator var2 = allEmployees.iterator(); var2.hasNext(); var1 = (Person)var2.next()) { } } private Payroll() { } static { Payroll var0 = new Payroll(); INSTANCE = var0; boolean var1 = false; allEmployees = new ArrayList(); } }
对象声明能够继承类和实现接口。对于实现并不包含任何状态的时候颇有用,如:实现java.util.Comparator接口,只需一个单独的Comparator实例比较对象。
object FileComparator : Comparator<File>{ override fun compare(o1: File, o2: File): Int { return o1.path.compareTo(o2.parent, ignoreCase = true) } }
能够在类中声明对象,这样对象也一样只有一个单一实例
// 使用嵌套类实现Comparator data class Person(val name: String) { object NameComparator : Comparator<Person> { override fun compare(o1: Person, o2: Person): Int { return o1.name.compareTo(o2.name) } } } >>> val list = listOf(Person("Kerwin"), Person("Bob")) >>> println(list.sortedWith(Person.NameComparator)) // [Person(name=Bob), Person(name=Kerwin)]
kotlin中的类不能拥有静态成员,java的static关键字并非kotlin语言的一部分。
kotlin对象声明在其余状况下替代java的静态方法,同时还包括静态字段
kotlin依赖包级别函数在大多数状况下可以替代java的静态方法
推荐使用顶层函数,但顶层函数不能访问类的private成员。
在类中定义的对象之一可使用一个特殊的关键字来标记:companion
class A { // 伴生对象 companion object { fun bar() { println("companion object called.") } } } // 能够直接经过类名来访问这个伴生对象的方法和属性 // 这种调用方式有点像java中静态方法的调用 >>> A.bar()
伴生对象能够访问类中的全部的private成员,包括private构造方法。使用工厂方法来建立类实例,下面User实例就是经过工厂方法来建立,而不是经过多个构造方法。伴生对象成员在子类中不能被重写。
// 将主构造方法标记为私有的,该类不被其余代码实例化 class User private constructor( val nickname: String ) { // 声明伴生对象 companion object { fun newSubscribingUser(email: String) = User(email.substringBefore("@")) // 用工厂方法建立一个新用户 fun newFacebookUser(accountId: Int) = User(accountId.toString()) } } // 经过类名调用 companion object 的方法 >>> val user = User.newSubscribingUser("kerwin0618@163.com") >>> println(user.nickname) kerwin0618
伴生对象是一个声明在类中的普通对象。能够有名字,实现一个接口或者有扩展函数或者属性。
class Person( val name: String ) { // 伴生对象能够有名字,若是省略伴生对象名字,默认名字为Companion // 伴生对象没有命名,在java中,能够经过Person.Companion.fromJSON("");访问 companion object Loader { fun fromJson(jsonText: String) : Person { return Person(jsonText) } } } // 能够经过两种方式来调用fromJson >>> Person.Loader.fromJson("{name: 'kerwin'}") >>> Person.fromJson("{name: 'kerwin'}")
在伴生对象中实现接口:
interface JSONFactory<T> { fun fromJSON(jsonText: String): T } class Person( val name: String ) { // 在伴生对象中实现接口 companion object : JSONFactory<Person> { override fun fromJSON(jsonText: String): Person { return Person(jsonText) } } } fun <T> loadFromJSON(factory: JSONFactory<T>) : T { return factory.fromJSON("{name: 'kerwin'}") } // 将伴生对象实例传入函数中。Person类的名字被看成JSONFactory的实例 >>> loadFromJSON(Person)
若是要和java代码一块儿工做,须要类中的成员是静态的,能够在对应的成员上使用 @JvmStatic 注解达到目的。若是想声明一个static字段,能够在一个顶层属性或者声明在object中的属性上使用 @JvmField 注解。
伴生对象扩展:扩展函数定义经过类的实例调用方法 ,但须要定义能够经过类调用方法怎么办呢?
// 为伴生对象定义一个扩展函数 data class Person( val firstName: String, val lastName: String ) { // 声明一个空的伴生对象 companion object { } } // 声明一个扩展函数 fun Person.Companion.fromJSON(jsonText: String) : Person { return Person("xu", "jinbing") }
object 关键字不只仅能用来声明单例式的对象,还能用来声明匿名对象。匿名对象代替java中匿名内部类的用法。
// 使用匿名对象来实现事件监听器 val button = Button() button.setOnClickListener( // 声明一个实现OnClickListener的匿名对象 object : OnClickListener { // 重写OnClickListener的方法 override fun onClick(view: View) { } } ) // 给对象分配一个名字 val listener = object : OnClickListener { override fun onClick(view: View) { } }
kotlin的匿名对象能够实现多个接口或者不实现接口。
与对象声明不一样,匿名对象不是单例的,每次对象表达式被执行都会建立一个新的对象实例。
对象表达式中的代码能够访问建立它的函数中的变量,与java不一样,访问并无被限制在final变量,还能够在对象表达式中修改变量的值
// 匿名对象访问局部变量 fun countClicks(button: Button) { // 声明局部变量 var clickCount = 0 button.setOnClickListener(object : OnClickListener { override fun onClick(view: View) { // 更新变量的值 clickCount++ } }) }
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)