类、对象和接口
一、定义类的继承结构
一)Kotlin中的接口
Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态。
1 interface Clickable{ 2 fun click() 3 fun showoff()=println("It‘s show time!") 4 } 5 6 interface Focusable{ 7 fun setFocus(b: Boolean)= 8 println("I ${if (b) "got" else "lost"} focus.") 9 fun showoff()= println("Kotlin") 10 } 11 12 class Button: Clickable, Focusable{ 13 //override用法同[email protected]但为强制要求 14 override fun click() { 15 println("I was clicked!") 16 } 17 18 //这里如果没有显示地实现showoff,会得到编译错误 19 override fun showoff(){ 20 //Java中的用法:Clickable.super.showoff(); 21 /*super<Clickable>.showoff() 22 super<Focusable>.showoff()*/ 23 println("Method of son.") 24 } 25 }
二)控制继承的修饰符:open、final和abstract:默认为final
1 /** 2 * Java中的类和方法默认是open的,而Kotlin中默认是final的。 3 * 如果你想允许一个类创建一个子类,需要使用open修饰符来标识这个类 4 * 方法与属性也是如此。 5 * */ 6 open class Button: Clickable{ 7 //这个函数是open的 8 override fun click() { 9 println("Clicking it") 10 } 11 //这个函数不再是open的了 12 final override fun showoff()= println("Show off") 13 open fun animate(){} 14 } 15 16 /** 17 * abstract类与Java中的相同,不能被实例化。 18 * 19 * */ 20 abstract class Animated{ 21 //这个函数是抽象的,必须被子类实现,所以也是open的 22 abstract fun animated() 23 24 //抽象类中的非抽象函数并不是默认open的,但可以标注为open的 25 open fun stopAnimating()= println("Stop it!") 26 }
三)可见性修饰符:public,protected,internal和private,默认为public
可见性修饰符帮助控制对代码库中声明的访问。Java中默认可见性为-----包私有,在Kotlin只把包作为在命名空间里组织代码的一种方式使用,并没有将其用作可见性控制。
作为替代Kotlin中提供了一种新的修饰符---internal,表示"只在模块(一组一起编译的Kotlin文件)内部可见"。另外一个区别就是Kotlin允许在顶层声明中使用private可见性,
使这些声明只会在声明他们的文件中可见。
1 open internal class Button{ 2 private fun click()= println("Hey!") 3 protected fun showoff()= println("show it") 4 } 5 6 /* 7 fun Button.fMethod(){ //错误:public成员暴露了其"internal"接收者类型B 8 yell() //错误 9 showoff() //错误 10 } 11 */ 12 13 internal fun Button.fMethod(){ 14 /*showoff() 任然错误*/ 15 }
另一个与Java可见性不同的是:一个外部类不能看到其内部类或嵌套类中的private成员。
四)内部类和嵌套类:默认为嵌套类
只需要记住Kotlin中没有显示修饰符的嵌套类与Java中的static嵌套类是一样的,如果要使其持有外部类的引用的话需要使用inner修饰符。
引用外部类实例语法:[email protected]
五)密封类:定义受限的类继承结构
为父类添加一个sealed修饰符,对可能创建的子类做出严格限制,所有的直接子类必须嵌套在父类中。
请思考这样做的好处。
二、声明一个带默认构造方法或属性的类
一)初始化类:主构造方法和初始化语句块
1 class User0(val name: String="tang"/*可提供默认值*/) /*简便写法,其中val关键字意味着 2 相应的属性会用构造方法的参数来初始化*/ 3 4 open class User constructor(name: String)/*带参数的主构造方法,constructor 5 用来声明一个主构造方法和一个从构造方法*/{ 6 val name:String 7 8 init { //初始化语句块,因为主构造方法的语法限制,所有有了初始化语句 9 //一个类中可以声明多个初始化语句 10 this.name=name 11 } 12 } 13 14 //如果一个类具有父类,主构造方法同样需要初始化父类。 15 // 即使父类构造函数没有任何参数,也要显示地调用构造函数。 16 class TwitterUser(name: String) : User(name){} 17 18 //如果要确保类不被实例化,可以把构造函数标记为private 19 class Person private constructor(val name: String)
二)构造方法:用不同的方法来初始化父类
1 //没有主构造函数 2 open class View{ 3 constructor(ctx: String) 4 constructor(ctx: String, attr: String) 5 } 6 7 class MyButton : View{ 8 constructor(ctx: String) :this(ctx,"s") 9 10 constructor(ctx: String,attr: String) : super(ctx,attr){ 11 /*some code*/ 12 } 13 }
三)实现在接口中声明的属性
1 /*接口可以包含抽象属性声明*/ 2 interface User{ 3 val nickname: String 4 /*接口还可包含具有getter和setter的属性,只要他们没有引用一个支持字段*/ 5 val age: Int 6 get() = Random().nextInt(100) 7 } 8 9 /*在实现了接口的类中必须override该属性*/ 10 class QQUser (override val nickname: String) : User 11 12 class BaiduUser(val email: String) : User{ 13 override val nickname: String 14 get() = "Baidu $email" 15 }
四)通过getter或setter访问支持字段
1 class User(val name: String){ 2 var address: String="unspecified" 3 //在set函数体中,使用了标识符field来访问支持字段的值。 4 //在get函数中只能读取它,在set函数中可以修改它。 5 set(value: String) { 6 println(""" 7 Address was changed for $name: "$field" -> "$value".""".trimIndent()) 8 } 9 } 10 11 fun main(args: Array<String>) { 12 val user=User("Tom") 13 user.address="Back Street 221" 14 user.address="Beijing hu tong 222" 15 /*Address was changed for Tom: "unspecified" -> "Back Street 221". 16 Address was changed for Tom: "unspecified" -> "Beijing hu tong 222".*/ 17 18 }
五)修改访问其可见性
1 class LengthCounter{ 2 var counter: Int=0 3 private set //声明为private,不能再类外部修改这个属性 4 5 fun addWord(word: String){ 6 counter+=word.length 7 } 8 }
三、编译器生成的方法:数据类和类委托
一)通用的对象方法
1.toString()
2.equals():在Kotlin中"=="相当于Java中的equals,"==="相当于Java中的"=="(比较引用)
3.hashCode()方法通常与equals方法一起被重写(如果两个对象相等,它们通常必须有相同的hash值)。
1 class Client(val name: String,val postalCode: Int){ 2 override fun equals(other: Any?): Boolean { 3 if (other==null || other !is Client) return false 4 return name==other.name && postalCode==other.postalCode 5 } 6 7 override fun toString(): String { 8 return "Client($name,$postalCode)" 9 } 10 11 override fun hashCode(): Int { 12 return name.hashCode()*12+postalCode 13 } 14 }
二)数据类:自动生成通用方法实现
如果为类添加data修饰符,上述通用方法将会被自动生成好。其中
1.equals方法:检测所以主构造函数属性的值是否相等。
2.hashCode方法:返回一个根据所有主构造函数中属性值生成的哈希值。
注意:没有在主构造函数中声明的属性将不会加入到相等性检测和哈希值计算中去。
data class User(val name: String,val age:Int,val gender: String)
三)类委托:使用"by"关键字
class TangCollection<T>( val innerList: Collection<T> = ArrayList<T>() ) : Collection<T> by innerList {}
四)"object"关键字:将声明一个类与创建一个实例结合起来
object关键字:定义一个类的同时创建一个实例
一)对象声明:创建单例
/* * 在定义的时候就被创建,并且为一个单例 * */ object User { val name: String = "Tang" val age: Int = 8 }
二)伴生对象:工厂方法和静态成员的地盘
class A { companion object { fun t(){ println("Tang") } } } fun main(args: Array<String>) { A.t() }
三)作为普通对象使用的伴生对象