Kotlin 进阶之路4 面向对象
1.面向对向编程
本质上就是解决如何用程序描述世界的问题
讨论如何把实际存在东西映射成程序的类和对象
一种程序设计的思路、思想、方法
/** * 面向对向编程 * 对某种事物进行抽象化,称之为建模 * */ //kotlin 定义:class 类名 constructor(属性列表){更多的属性和方法描述} //构造器:用来设置类新实例的出厂配置 //类要被继承需要加open open class Chinese constructor(val sex: Boolean, var region: String) { //普通属性,与变量定义相似 open val skin = "yellow" //组合属性,由其它属性计算而来(get) val avgLife: Double get() { when (this.region) { "Shanghai" -> { return 82.4 } "Beijing" ->{ return 80.0 } else -> { return 72.1 } } } //组合属性反过来可以影响其它属性(set,可选) this代表实例 var avgSalary : Int get(){ when(this.region){ "Shanghai" ->{ return 4000} "Beijing" ->{ return 5100} else ->{ return 3500} } } set(value) { when (value) { in 4500..Int.MAX_VALUE -> { this.region ="Beijing" } in 2800..4100->{ this.region ="Shanghai" } else -> { this.region = "homeland" } } } //方法:厨艺 open fun cook(): Unit { val meau = arrayOf("宫爆鸡丁","番茄炒鸡蛋","葱爆牛肉") val desc = meau.reduce{s1,s2->s1+","+s2} println("我会${desc}") } } class BeijingPeople(sex: Boolean, region: String = "Beijing") : Chinese(sex, region) { val greatBuilding="the Forbidden City" //override覆盖属性好方法 override val skin ="BeijingYellow" override fun cook() { super.cook() val meau = arrayOf("北京烤鸭","北京炸酱面","北京包子") val desc = meau.reduce{s1,s2->s1+","+s2} println("我还会${desc}") } } class ShanghaiPeople(sex: Boolean, region: String = "Shanghai") : Chinese(sex, region){ val greatBuilding="the Oriental Pearl Tower" override val skin ="ShanghaiYellow" override fun cook() { super.cook() val meau = arrayOf("大闸蟹","小汤包","芝士焗面") val desc = meau.reduce{s1,s2->s1+","+s2} println("我还会${desc}") } } fun main(args: Array<String>) { //实例化一个类 val Lili = BeijingPeople(true) Lili.avgSalary = 6000 println(Lili.region) println(Lili.avgSalary) Lili.cook() println() val Jack = ShanghaiPeople(true) Lili.avgSalary = 5000 println(Jack.region) println(Jack.avgSalary) Jack.cook() } 复制代码
Beijing 5100 我会宫爆鸡丁,番茄炒鸡蛋,葱爆牛肉 我还会北京烤鸭,北京炸酱面,北京包子 Shanghai 4000 我会宫爆鸡丁,番茄炒鸡蛋,葱爆牛肉 我还会大闸蟹,小汤包,芝士焗面 复制代码
2.抽象类和接口
抽象类:理解为半成品
接口:类似于协议或者某种成品就有的功能
接口
不能有状态
必须有类对其进行实现后实现
接口,直观理解就是一种约定
Kotlin的接口与Objective-C的Protocol比较类似 复制代码
举例
interface InputDevice{ fun input(event: Any) } 复制代码
抽象类
实现了一部分协议的半成品
可以有状态,可以有方法实现
必须由子类继承后使用
抽象类和接口的共性
比较抽象,不能直接实例化
有需要子类(实现类)实现的方法
父类(接口)变量可以接受子类(实现类)的实例赋值
抽象类和接口的区别
抽象类有状态,接口没有状态
抽象类有实现方法,接口只能有无状态的默认实现
抽象类只能单继承,接口可以多实现
抽象类反应本质,接口提现能力
interface InputDevice { fun input(event: Any) } interface USBInputDevice : InputDevice interface BLEInputDevice : InputDevice abstract class USBMouse(val name:String) : USBInputDevice,OpticalMouse { override fun input(event: Any) { } override fun toString(): String { return name } } interface OpticalMouse{ } class LogitechMouse:USBMouse("罗技鼠标"){ } class Computer { fun addUSBInputDevice(inputDevice: USBInputDevice) { //插入输入设备 println("add usb input device:$inputDevice") } fun addBLEInputDevice(inputDevice: BLEInputDevice) { //插入输入设备 println("add input device:$inputDevice") } fun addInputDevice(inputDevice: InputDevice) { when (inputDevice) { is BLEInputDevice -> { addBLEInputDevice(inputDevice) } is USBInputDevice -> { addUSBInputDevice(inputDevice) } else -> { throw IllegalArgumentException("输入设备不支持") } } } } fun main(args: Array<String>) { val computer = Computer() val mouse = LogitechMouse() computer.addInputDevice(mouse) } 复制代码
add usb input device:罗技鼠标 复制代码
3.继承(实现)
继承(实现)语法要点
父类需要open才可以被继承
父类方法、需要open才可以被覆写
接口、接口方法、抽象类默认为open
覆写父类(接口)成员需要override关键字
class D:A(),B,C
注意继承类时实际上调用了父类的构造方法
类只能单继承,接口可以多实现
接口代理
class Manager(driver:Driver):Driver by driver
接口方法实现交给代理类实现
接口方法冲突
接口方法可以有默认实现
签名一致且返回值有冲突
子类(实现类)必须覆写冲突方法
super<[父类(接口)名]>.方法名
abstract class Person(open val age: Int) { open fun work() { } } class Coder(age: Int) : Person(age) { override val age: Int get() = 0 override fun work() { super.work() println("我是程序员") } } class Doctor(override val age: Int) : Person(age) { override fun work() { super.work() println("我是医生") } } fun main(args: Array<String>) { val person = Coder(28) person.work() println(person.age) val person2 = Doctor(25) person2.work() println(person2.age) } 复制代码
我是程序员 0 我是医生 25 复制代码
class Manager : Driver, Writer { override fun write() { } override fun drive() { } } //写法一 //class SeniorManager(val driver: Driver,val writer: Writer):Driver,Writer{ // override fun drive(){ // driver.drive() // } // override fun write(){ // writer.write() // } //} //简写二 class SeniorManager(val driver: Driver, val writer: Writer) : Driver by driver, Writer by writer class CarDriver : Driver { override fun drive() { println("开车了") } } class PPTWriter :Writer{ override fun write(){ println("做PPT了") } } interface Driver { fun drive() { } } interface Writer { fun write() { } } fun main(args: Array<String>) { val driver = CarDriver() val writer = PPTWriter() val seniorManager = SeniorManager(driver,writer) seniorManager.drive() seniorManager.write() } 复制代码
开车了 做PPT了 复制代码
interface InputDevice { fun input(event: Any) } interface USBInputDevice : InputDevice interface BLEInputDevice : InputDevice abstract class USBMouse(val name:String) : USBInputDevice,OpticalMouse { override fun input(event: Any) { } override fun toString(): String { return name } } interface OpticalMouse{ } class LogitechMouse:USBMouse("罗技鼠标"){ } class Computer { fun addUSBInputDevice(inputDevice: USBInputDevice) { //插入输入设备 println("add usb input device:$inputDevice") } fun addBLEInputDevice(inputDevice: BLEInputDevice) { //插入输入设备 println("add input device:$inputDevice") } fun addInputDevice(inputDevice: InputDevice) { when (inputDevice) { is BLEInputDevice -> { addBLEInputDevice(inputDevice) } is USBInputDevice -> { addUSBInputDevice(inputDevice) } else -> { throw IllegalArgumentException("输入设备不支持") } } } } fun main(args: Array<String>) { val computer = Computer() val mouse = LogitechMouse() computer.addInputDevice(mouse) } 复制代码
add usb input device:罗技鼠标 复制代码
4.Kotlin和Java可见性对比
Kotlin | Java |
---|---|
public | public |
private | priavte |
protected | protected |
- | default(包内可见) |
internal(模块类可见) | - |
5.对象声明和表达式
object
只有一个实例的类
不能自定义构造方法
可以实现接口、继承父类
本质上就是单例模式最基本的实现
/** * 有时候只要对某各类进行改造,供零时使用,避免继承 * 对象声明和表达式就很有作用 * * 面向对象编程的优化,避免一些继承导致的代价过高,保持代码的整洁 */ //对中国人来说,这个类,可能各省人适合继承 open class Chinese(var name: String) { open val skin = "yellow" } fun main(args: Array<String>) { //但如果外国人入籍,就不适合用继承 //对象表达式:val 对象名 = object : 类,接口 {//属性或方法的override定义} val Jack= object : Chinese ("Jack Marry") { override val skin = "white" } println(Jack.skin) //纯对象表达式:临时使用,无须继承任何类 val loc = object { var x = 100 var y = 200 } println(loc.x) //相当于调用函数 NetworkRequestManager.register() //半生对象的方法,与类关联性强 IDCard.create() } //对象声明,不能用在函数中 //一般用于对其他类使用上的包装 object NetworkRequestManager{ fun register(){ println("连接网络注册中...") } } //半生对象:一般用于创建一个类的实例"工厂"方法 //Java中的 静态成员 class IDCard{ companion object { fun create() = IDCard() } } 复制代码
white 100 连接网络注册中... 复制代码
6.半生对象与静态成员
每个类可以对应一个伴生对象
伴生对象的成员全局独一份
伴生对象的成员类似Java的静态成员
静态成员考虑用包级函数、变量替代
JvmField和JvmStatic的使用
fun main(args: Array<String>) { val a = minOf(args[0].toInt(), args[1].toInt()) val latitude = Latitude.ofDouble(3.0) val latitude2 = Latitude.ofLatitude(latitude) println(Latitude.TAG) } class Latitude private constructor(val value: Double) { companion object { @JvmStatic fun ofDouble(double: Double): Latitude { return Latitude(double) } fun ofLatitude(latitude: Latitude): Latitude { return Latitude(latitude.value) } @JvmField val TAG:String = "Latitude" } } 复制代码
Latitude 复制代码
JvmStatic JvmField 用于Java类调用
public class StaticJava { public static void main(String[] args){ Latitude latitude = Latitude.ofDouble(4); System.out.println("Java:"+Latitude.TAG); } } 复制代码
Java:Latitude 复制代码
6.方法重载Overloads 和默认参数
方法重载Overloads
名称相同、参数不同的方法
Jvm函数签名的概念:函数名、参数列表
跟返回值没有关系
默认参数
为函数参数设定一个默认值
可以为任意位置的参数设置默认值
函数调用产生混淆时用具名参数
两者关系
避免定义关系不大的重载
方法重载最好能转化为默认参数
不好的设计:
- List.remove(int) - List.remove(Object) 复制代码
//方法重载和函数名有和参数列表有关系,和函数返回值没关系 class Overloads { // fun a(): Int { // return 0 // } @JvmOverloads //默认参数 是给java调用的 fun a(int: Int = 0): Int { return int } } fun main(args: Array<String>) { val overloads = Overloads() overloads.a() val integerList = ArrayList<Int>() integerList.add(555) integerList.add(2) integerList.add(3) integerList.add(4) integerList.add(9) integerList.add(45) println(integerList.toString()) integerList.removeAt(1) integerList.remove(9) println(integerList.toString()) } 复制代码
[555, 2, 3, 4, 9, 45] [555, 3, 4, 45] 复制代码
7.扩展成员
Java调用扩展成员类似调用静态方法
为现有类添加方法、属性
- fun X.y():Z{...} - val X.m 主义扩展属性不能初始化,类似接口属性 复制代码
fun main(args: Array<String>) { println("abc" * 16) "abc".b = 5 println("abc".b) } operator fun String.times(int: Int): String { val stringBuilder = StringBuilder() for (i in 0 until int) { stringBuilder.append(this) } return stringBuilder.toString() } val String.a: String get() = "abc" var String.b: Int set(value) { } get() = 5 复制代码
abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc 5 复制代码
8.属性代理
定义方法:
- val/var <property name>:<Type> by <expression> 复制代码
代理者需要实现相应的setValue/getValue方法:
lazy原理剖析
9.数据类
再见,JavaBean
默认实现的copy,toString等方法
componentN方法
allOpen和noArg插件 在编译器 把final关键字去掉
import com.haocai.kotlindemo.annotations.Poko @Poko data class Country(val id: Int, val name: String) class ComponentX { operator fun component1(): String { return "您还,我是" } operator fun component2(): Int { return 1 } operator fun component3(): Int { return 1 } operator fun component4(): Int { return 0 } } fun main(args: Array<String>) { val china = Country(0, "中国") println(china) println(china.component1()) println(china.component2()) val (id, name) = china println(id) println(name) // for ((index, value) in args.withIndex()) { // println(index) // println(value) // } val componentX = ComponentX() val (a, b, c, d) = componentX println("$a $b $c $d") } 复制代码
10.内部类
定义在类内部的类
与类成员有相似的访问控制
默认是静态内部类,非静态用inner关键字
this@Outter,this@Inner的用法
没有定义名字的内部类
类名编译时生成,类似Outter$1.class
可继承父类、实现多个接口,与Java注意区别
Kotlin写法:
open class Outter { val a: Int = 0 inner class Inner { val a: Int = 5 fun hello() { println(this@Outter.a) } } } interface OnClickListener { fun onClick() } class View { var onClickListener: OnClickListener? = null } fun main(args: Array<String>) { val inner = Outter().Inner() inner.hello() val view = View() //匿名内部类 即可以实现接口,同时继承外部类(如,Outter()) view.onClickListener = object : Outter(),OnClickListener{ override fun onClick() { } } } 复制代码
0 复制代码
Java类似写法:
public class InnerClassJava { private int a; public static void main(String... args) { InnerClassJava innerClassJava = new InnerClassJava(); Inner inner = innerClassJava.new Inner(); //Inner inner = new Inner(); 报错 除非Inner 是静态 inner.hello(); View view = new View(); view.setOnClickListener(new OnClickListener() { @Override public void onClick() { } }); } public class Inner { public void hello() { System.out.println(InnerClassJava.this.a); //非静态内部类可以持有外部类的状态 } } } 复制代码
0 复制代码
11.枚举
实例可数的参数,注意枚举也是类
可以修改构造,添加成员
可以提升代码的表现力,也有一定的性能开销
enum class LogLevel(val id: Int) { VERBOSE(0), DEBUG(1), INFO(2), WARN(3), ERROR(4), ASSERT(5); fun getTag(): String { return "$id,$name" } override fun toString(): String { return "$name,$ordinal" } } class LogLevel2 protected constructor() { companion object { val VERBOSE = LogLevel2() val DEBUG = LogLevel2 val INFO = LogLevel2 val WARN = LogLevel2 val ERROR = LogLevel2 val ASSERT = LogLevel2 } } fun main(args: Array<String>) { println(LogLevel.INFO.getTag()) println(LogLevel.DEBUG.ordinal) LogLevel.values().map(::println) } 复制代码
2,INFO 1 VERBOSE,0 DEBUG,1 INFO,2 WARN,3 ERROR,4 ASSERT,5 复制代码
12.密封类
子类可数(从v1.1开始,只需要与密封类在同一个文件中)
枚举是实例可数
sealed class PlayerCmd{ class Play(val url:String ,val position:Long = 0):PlayerCmd() class Seek(val position:Long ):PlayerCmd() object Pause:PlayerCmd() object Resume:PlayerCmd() object Stop:PlayerCmd() } enum class PlayerState{ IDLE,PAUSE,PLAYING }
作者:冬日毛毛雨
链接:https://juejin.cn/post/7025867496448081927