一、抽象类
要定义一个抽象类,你需要在定义之前加上abstract关键字,即包含了具体的函数实现,也包含没有函数实现。
//定义抽象类
abstract class Book{
//未实现函数
abstract fun introduce():String
//实现函数
fun classification()="Book-技术"
}
//继承类,抽象类无需使用open关键字
class AndroidBook: Book() {
override fun introduce(): String {
return "AndroidBook-Kotlin"
}
}
fun main() {
//继承类实现函数
println(AndroidBook().introduce())
//抽象类实现函数
println(AndroidBook().classification())
}
二、嵌套类(内部类)
如果一个类只对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起是合乎逻辑的,可以使用嵌套类。
class SanGuo {// 外部类
class WeiGuo(var name: String) {
//嵌套类
fun caption() = println("$name,是魏国滴")
}
class ShuGuo(var name: String) {
//嵌套类
fun caption() = println("$name,是蜀国滴")
}
}
fun main() {
//外部类.嵌套类直接构建出嵌套类实例。
SanGuo.WeiGuo("张辽").caption()
SanGuo.ShuGuo("赵云").caption()
}
注意:在Kotlin中可以通过外部类名
.
嵌套类,直接构建出嵌套类的实例。如果是Java的内部类,那只有在静态类才能通过类名直接调用。
三、抽象类
3.1 object 关键字
-
使用 object 关键字,你可以定义一个只能产生一个实例的类-单例
-
使用object关键字有三种方式
-
对象声明
-
对象表达式
-
伴生对象(companion 关键字)
-
用对象表达式和对象声明来实现创建一个对某个类做了轻微改动的类的对象,且不需要去声明一个新的子类。
3.2 对象声明
使用 object 关键字来声明一个对象,通过对象声明来获得一个单例。
作用:对象声明有利于组织代码和管理状态,尤其是管理整个应用运行生命周期内的某些一致性状态。
/*使用object声明对象*/
object AppUtils{
init {
println("AppUtils init")
}
fun doData(){
println("AppUtils doData")
}
}
//使用
fun main() {
//AppUtils既是类名又是实例名
AppUtils.doData()
}
当时当你定义两个不同的变量来获取这个对象时,你会发现你并不能得到两个不同的变量。也就是说通过这种方式,我们获得一个单例。
3.3 对象表达式
通过对象表达式实现一个匿名内部类的对象。如果你需要某个现有类的一种变体实例,但只需用一次就行了,事实上,对于这种用完就丢的类实例,连命名都可以省了。这个对象表达式是XX的子类,这个匿名类依然遵循object关键字的一个规则,即一旦实例化,该匿名类只能有唯一一个实例存在。
open class Uesr{
var name = "帅次"
var age = 16
open fun doInfo() = "Uesr doInfo"
}
//使用
fun main() {
//User类别忘记加open关键字
val user = object : Uesr(){
override fun doInfo()= "User object"
}
println(user.doInfo())
}
不定义类,直接得到一个对象
//通过对象表达式可以越过类的定义直接得到一个对象
val vipUser = object {
var name = "Kotlin"
var age = 15
}
println(vipUser.name)
println(vipUser.age)
3.4 伴生对象
如果你想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象,使用companion修饰符,你可以在一个类定义里声明一个伴生对象,一个类里只能有一个伴生对象。
例如:例如东皇太一刚出生就有混沌钟,东皇钟就是东黄天一的伴生至宝。
open class `东皇太一`{
companion object {
val lq = "刚出生就有先天至宝"
fun `混沌钟`() = lq
}
}
//使用
fun main() {
//`东皇太一`类直接调用伴生对象函数。
println(东皇太一.混沌钟())
}
-
只有调用
东皇太一
类 或者调用混沌钟
函数,伴生对象才会实例化 -
节省内存:
- 如果不调用那么伴生对象就不会实例化也不会占用内存。
- 无论你调用多少次
东皇太一
类 或者调用混沌钟
函数,伴生对象只会实例化一次。
四、接口
为什么要使用Kotlin接口?
- 使用接口支持多重继承功能。
- 它可以用来实现松耦合。
- 它用于实现抽象。
一个类只能扩展一个超类但可以实现多个接口。父类或接口实现的扩展是在子类中使用(:)运算符完成的。
4.1 接口定义
interface 关键字可以定义接口,定义接口和定义类类似, 允许接口( interface ) 的函数有默认实现。
//interface 关键字可以定义接口
interface Tea {
//定义属性
val type: String
val price: Int
//定义函数
fun effect(): String
fun action(): String
}
//实现接口 冒号:+接口名
//重写属性和函数
class GreenTea(var place: String, override var type: String, override val price: Int) : Tea {
override fun effect(): String {
return "${type}-产自-${place}-有抑制心血管疾病的功效,$price"
}
override fun action(): String {
return "${type}-产自-${place}-有延缓衰老的作用,$price"
}
}
fun main() {
//使用实现接口的类
println(GreenTea("西湖", "绿茶", 128).effect())
}
默认情况下,仅在没有函数(函数只声明未实现)的情况下声明的方法是抽象的。如上面的:effect()、action()。
4.2 默认实现
你可以在接口里提供默认属性的getter方法和函数实现。
interface Tea2 {
//定义属性
val type: String
val price: Int //提供默认属性的getter
get() = 500
//定义函数
fun effect(): String
fun action(): String {//默认函数实现
return "${type}-有延缓衰老的作用,$price"
}
}
//实现接口 冒号:+接口名
//必须实现接口中所有没有默认实现的函数和属性
class GreenTea2(val place:String,override val type: String) : Tea2 {
override fun effect(): String {
return "${type}-产自-${place}-有抑制心血管疾病的功效,$price"
}
}
fun main() {
//使用实现接口的类
println(GreenTea2("西湖", "绿茶").effect())
//接口函数实现
println(GreenTea2("西湖", "绿茶").action())
}
4.3 小结
虽然接口可以提供默认属性的getter方法和函数实现,但是绝大部分接口只提供功能和函数的声明,就算提供属性也是常量。