Kotlin入门之面向对象
一 面向对象-抽象类与接口(abstract,interface)
接口: 一个协议
抽象类: 一个半成品
二 子承父业-继承
2.1 继承(实现)语法要点
- 类的继承/实现语法,使用冒号 class D : A(),B,C
- 抽象类用冒号(:)继承 抽象方法用override复写
- 注意继承类时,实际调用了父类构造方法
- 类只能单继承,接口可以多实现
- 父类需要 open 才可以被继承
- 父类方法及属性需要open才可以被覆写
- 接口,接口方法,抽象类默认为open
- 覆写父类成员/接口成员需要用到override关键字
// 定义一个抽象类 构造方法参数用open修饰表示可重写
abstract class Person(open val age: Int){
// 定义一个抽象方法
abstract fun work()
}
// 子类继承父类
class Doctor(age: Int): Person(age){
// 如果继承的是抽象类,必须重写所有的抽象方法
override fun work() {
println("我是医生,我在给病人看病")
}
}
// 子类继承父类
class MaNong(age: Int): Person(age){
// 重写参数值
override val age: Int
get() = 0
override fun work() {
println("我是码农,我在写代码")
}
}
fun main(args: Array<String>) {
val person: Person = MaNong(23)
person.work()
println(person.age)
val person2 : Person = Doctor(24)
person2.work()
println(person2.age)
}
我是码农,我在写代码
0
我是医生,我在给病人看病
24
2.2 接口代理
1 接口代理语法 class Manager(driver:Driver):Driver by driver
2 通过接口代理,让接口方法的实现交给代理类实现
// 普通经理, 实现接口(用冒号)
class Manager: Driver, Writer {
override fun write() {
}
override fun drive() {
}
}
// 资深经理 使用接口代理
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()
}
2.3 接口方法冲突
1 接口方法可以有默认实现
2 签名一致且返回值相同的冲突
3 子类(实现类)必须覆写冲突方法
4 super<父类(接口)名>.[方法名]([参数列表])
当接口的签名一样(方法名一样,参数列表一样),返回值类型也一样的处理方法,使用super<类名>.方法名
// 定义一个抽象父类
abstract class A{
open fun x(): Int = 5
}
// 定义接口B
interface B{
fun x(): Int = 1
}
// 定义皆苦C
interface C{
fun x(): Int = 0
}
// D 继承类,实现接口
class D(var y: Int = 0): A(), B, C{
override fun x(): Int {
println("call x(): Int in D")
if(y > 0){
return y
}else if(y < -200){
return super<C>.x()
}else if(y < -100){
return super<B>.x()
}else{
return super<A>.x()
}
}
}
fun main(args: Array<String>) {
println(D(3).x())
println(D(-10).x())
println(D(-110).x())
println(D(-10000).x())
}
call x(): Int in D
3
call x(): Int in D
5
call x(): Int in D
1
call x(): Int in D
0
三 类及其成员变量的可见性(private protected internal public)
可见行对比
class House
class Flower
class Countyard{
// private 类私有,只能在这个类中调用,在其他地方不可调用
private val house: House = House()
private val flower: Flower = Flower()
}
class ForbiddenCity{
// internal 模块内可用,只在当前Module中可用
internal val houses = arrayOf(House(), House())
// 默认public 任何地方都可调用
val flowers = arrayOf(Flower(), Flower())
}
fun main(args: Array<String>) {
val countyard = Countyard()
val fc = ForbiddenCity()
println(fc.flowers)
}
四 object
1 只是一个实例的类
2 不能自定义构造方法
3 可以实现接口,继承父类
4 本质上就是单例模式最基本的实现
class Driver
// 定义接口 是否有外接硬盘插入
interface OnExternalDriverMountListener{
fun onMount(driver: Driver)
fun onUnmount(driver: Driver)
}
// 定义抽象父类
abstract class Player
// 音乐播放器 继承父类,实现接口
object MusicPlayer: Player(), OnExternalDriverMountListener{
override fun onMount(driver: Driver) {
}
override fun onUnmount(driver: Driver) {
}
// 成员变量
val state : Int = 0
// 成员方法
fun play(url : String){
}
// 成员方法
fun stop(){
}
}
五 伴生对象与静态成员
1 每个类可以对应一个伴生对象 使用companion object关键字
2 伴生对象的成员全局独一份
3 伴生对象的成员类似Java的静态成员
4 静态成员考虑用包级函数,包级变量替代
5 JvmField和JVMStatic的使用,这样可以在Java中调用
fun main(args: Array<String>) {
val latitude = Latitude.ofDouble(3.0)
val latitude2 = Latitude.ofLatitude(latitude)
println(Latitude.TAG)
}
// 纬度
class Latitude private constructor(val value: Double){
// 这个类的伴生对象
companion object{
@JvmStatic // @JvmStatic 便于在java中调用
fun ofDouble(double: Double): Latitude{
return Latitude(double)
}
// 相当于java中静态方法
fun ofLatitude(latitude: Latitude): Latitude{
return Latitude(latitude.value)
}
@JvmField // @JvmField 便于在java中调用
val TAG: String = "Latitude"
}
}
六 方法重载与默认参数
1 overloads 方法重载
2 名称相同,参数不同的方法
3 Jvm函数签名的概念:函数名,参数列表
4 方法重载与方法签名(方法名,参数列表)有关系,跟返回值类型没有关系
5 默认参数->为函数参数设定一个默认值
6 可以在任意位置的参数设置默认值
7 函数调用产生混淆时用具名参数
8 使用@JvmOverloads后,可在java中调用无参函数
9 注意要尽量避免定义关系不大的重载
class Overloads{
@JvmOverloads // @JvmOverloads 可在java中调用不传参数的a()
fun a(int: Int = 0): Int{
return int
}
}
fun main(args: Array<String>) {
val overloads = Overloads()
overloads.a(3)
val integerList = ArrayList<Int>()
integerList.add(13)
integerList.add(2)
integerList.add(3)
integerList.add(23)
integerList.add(5)
integerList.add(15)
integerList.add(50)
integerList.add(500)
println(integerList)
integerList.removeAt(1)
integerList.remove(5)
println(integerList)
}
java中调用
public class Bug {
public static void main(String... args) {
Overloads overloads = new Overloads();
// 如果在Kotlin中没有@JvmOverloads,在这里调用会报错
overloads.a();
}
}
七 扩展成员
1 为现有的类添加方法,属性
2 写法
fun X.y():Z{...}
val X.m //注意扩展属性不能初始化,类似接口属性
3 Java调用扩展成员类似调用静态方法
举例
fun main(args: Array<String>) {
println("abc" * 16)
"abc".b = 5
println("abc".b)
println("*" * 16)
}
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
****************
java中调用
public class ExtendsJava {
public static void main(String... args) {
System.out.println(ExtendsKt.times("abc", 16));
}
}
abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
八 属性代理
定义方法
val/var <property name>:<Type> by <expression>
代理者需要实现相应的 setValue/getValue 方法
lazy原理剖析
class Delegates{
val hello by lazy {
"HelloWorld"
}
val hello2 by X()
var hello3 by X()
}
class X{
private var value: String? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("getValue: $thisRef -> ${property.name}")
return value?: ""
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
println("setValue, $thisRef -> ${property.name} = $value")
this.value = value
}
}
fun main(args: Array<String>) {
val delegates = Delegates()
println(delegates.hello)
println(delegates.hello2)
println(delegates.hello3)
delegates.hello3 = "value of hello3"
println(delegates.hello3)
}
九 数据类data class(allOpen noArg插件 再见 JavaBean)
参考 Kotlin编译器插件
默认实现的copy,toString等方法
componentN 方法
allOpen 和 noArg 插件
使用data class需要安装2个官方插件
1 在build中配置插件
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.2.51'
id "org.jetbrains.kotlin.plugin.allopen" version "1.2.51"
id "org.jetbrains.kotlin.plugin.noarg" version "1.2.51"
}
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-noarg'
apply plugin: 'kotlin-allopen'
noArg{
annotation("com.wxyass.kotlin.annotations.PoKo")
}
allOpen{
annotation("com.wxyass.kotlin.annotations.PoKo")
}
2 在项目中新建PoKo.kt文件
annotation class PoKo
3 在data class添加注解@PoKo
/**
* 数据类
* Created by wxyass on 2/6/18.
*/
@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)
val componentX = ComponentX()
val (a, b, c, d) = componentX
println("$a $b$c$d")
}
Country(id=0, name=中国)
0
中国
0
中国
您好,我是 110
十 内部类(this@Outter this@Inner)
定义在类内部的类
与类成员有相似的访问控制
默认是静态内部类,非静态用inner关键字
this@Outter,this@Inner的用法
匿名内部类: 没有定义名字的内部类
匿名内部类的类名编译时生成,类似Outter$1.class
匿名内部类可继承父类,可实现多个接口,与Java注意区别
/**
* 内部类
* Created by benny on 4/4/17.
*/
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()
val view = View()
view.onClickListener = object : Outter(), OnClickListener{
override fun onClick() {
}
}
}
java中的内部类
public class InnerClassJava {
private int a;
public class Inner{
public void hello(){
System.out.println(InnerClassJava.this.a);
}
}
public static void main(String... args) {
InnerClassJava innerClassJava = new InnerClassJava();
View view = new View();
view.setOnClickListener(new OnClickListener() {
public void onClick() {
}
});
}
}
十一 枚举(enum)
实例可数的类,注意枚举也是类
可以修改构造,添加成员
可以提升代码的表现力,也有一定的性能开销
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"
}
}
fun main(args: Array<String>) {
println(LogLevel.DEBUG.ordinal)
LogLevel.values().map(::println)
println(LogLevel.valueOf("ERROR"))
}
class LogLevel2 protected constructor(){
companion object{
val VERBOSE = LogLevel2()
val DEBUG = LogLevel2()
val INFO = LogLevel2()
val WARN = LogLevel2()
val ERROR = LogLevel2()
val ASSERT = LogLevel2()
}
}
1
VERBOSE, 0
DEBUG, 1
INFO, 2
WARN, 3
ERROR, 4
ASSERT, 5
ERROR, 4
十二 密封类(seaied Class)
子类可数
<v1.1,子类必须定义为密封类的内部类
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
}