Kotlin学习笔记之面向对象
Posted mictoy_朱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin学习笔记之面向对象相关的知识,希望对你有一定的参考价值。
面向对象
接口定义
与java一样,使用interface表示,示例代码:
与java一样,kotlin定义类时要遵循单继承多实现的原则(即只能继承一个父类,可以实现多个接口)
kotlin中定义的类和方法默认都是final的,不可重写,如果要实现重写,需将对应方法类声明为open
示例代码:
package com.zhusp.kotlinuse
open class Person //可继承的类必须要用open关键字修饰
open fun work() //同样,里面的方法也需要用open修饰后才能在子类中重写,相当于java中的public
interface Driver
fun drive()
interface Writer
fun write()
class Manager(var driver:Driver,var writer: Writer):Driver by driver,Writer by writer //by 表示代理,此时无需再重写实现接口的方法
class Professor : Person()
override fun work()
class CarDriver : Driver
override fun drive()
println("轿车司机正在开车来")
class PPTWriter : Writer
override fun write()
println("秘书正在写PPT")
fun main()
val carDriver = CarDriver()
val pptWriter = PPTWriter()
val seniorManager = Manager(carDriver,pptWriter)
seniorManager.drive()
seniorManager.write()
运行结果
轿车司机正在开车来
秘书正在写PPT
object
object在kotlin中同class关键字一样,是表示一个类的,与普通class修饰的类不同的是,由object定义的类只有一个实例,并且不嫩自定义构造方法,其实它的本质就是java中对类的单例化。
示例代码:
interface MediaPlay
fun mount()
fun unMount()
abstract class Player
abstract fun deviceName()
abstract fun deviceNum()
object MusicPlayer:Player(),MediaPlay
override fun mount()
override fun unMount()
override fun deviceName()
println("I am MusicPlayer")
override fun deviceNum()
上面定义的MusicPlayer在其它kotlin可以直接通过该类名调用其中的方法:
MusicPlayer.deviceName()
运行:
I am MusicPlayer
object小结
- 只有一个实例的类
- 不能自定义构造方法
- 可以实现接口,继承父类
- 本质上就是单例最基本的实现
伴生对象与静态成员(public static void main)
- kotlin中每个类可以对应一个伴生对象
- 伴生对象的成员全局独一份
- 伴生对象的成员类似Java的静态成员
- 伴生对象用companion object表示
kotlin伴生对象代码示例:
fun main() //直接调用
println(StaticDemo.TAG)
println(StaticDemo.ofDouble(3.0))
class StaticDemo private constructor(val value:Double)
companion object //表示伴生对象,类似于java中对方法或变量声明static
@JvmStatic
fun ofDouble(double:Double):StaticDemo
return StaticDemo(double)
@JvmField
val TAG:String = "StaticDemo"
- 静态成员考虑用包级函数、变量替代
- JvmField和JvmStatic的使用(保证在java里调用时看起来跟普通java类的静态方法或变量一致)
扩展成员(二次加工)
类似于我们在Java中定义工具类及工具方法,不一样的是kotlin可以在原本存在的类(jdk或jar中的类,比如String)里直接扩展方法(就是所谓的二次加工)实现我们想要的功能的工具类方法,比如,现在需要一个循环字符串的工具方法(String本身没有这样的方法):
fun String.multiply(int :Int):String//扩展String里的方法
val strBuilder = StringBuilder()
for (i in 0 until int)
strBuilder.append(this)//这里this代表String
return strBuilder.toString()
fun main()
println("hello".multiply(6))//此时 就可以直接通过String调用multiply方法(实现二次加工)
运行结果:
hello,hello,hello,hello,hello,hello,
除了扩展方法,kotlin还可以扩展成员变量,需要注意的是,扩展变量时,需要用get()方法来设定默认值,例如:
val String.version: String
get() = "1.0.2"
这是,在使用String对象时就可以直接获取这个扩展变量
试一试:
println("Hello".version)
运行结果:
1.0.2
小结
数据类
- 解放Java中写JavaBean时大量代码的麻烦里
- 默认实现copy、toString等方法
- 生成对应属性个数的componentN方法
- 需添加allOpen和noArg插件(填坑插件)
示例代码:
kotlin实现数据类:
data class Country(val id:Int,val name:String)
编译后自动生成的对应的java代码:
public final class Country
private final int id;
@NotNull
private final String name;
public final int getId()
return this.id;
@NotNull
public final String getName()
return this.name;
public Country(int id, @NotNull String name)
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.id = id;
this.name = name;
public final int component1()
return this.id;
@NotNull
public final String component2()
return this.name;
@NotNull
public final Country copy(int id, @NotNull String name)
Intrinsics.checkParameterIsNotNull(name, "name");
return new Country(id, name);
// $FF: synthetic method
@NotNull
public static Country copy$default(Country var0, int var1, String var2, int var3, Object var4)
if ((var3 & 1) != 0)
var1 = var0.id;
if ((var3 & 2) != 0)
var2 = var0.name;
return var0.copy(var1, var2);
@NotNull
public String toString()
return "Country(id=" + this.id + ", name=" + this.name + ")";
public int hashCode()
int var10000 = this.id * 31;
String var10001 = this.name;
return var10000 + (var10001 != null ? var10001.hashCode() : 0);
public boolean equals(@Nullable Object var1)
if (this != var1)
if (var1 instanceof Country)
Country var2 = (Country)var1;
if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name))
return true;
return false;
else
return true;
从上面代码可知,kotlin中我们用一行代码就可以实现传统Java代码中繁杂JavaBean类的编写,这是kotlin给我们的便利之一
componentN是kotlin中除JavaBean中一般方法外特有的方法,使用示例:
fun main()
val china = Country(0,"China")
println(china)
println(china.component1())
println(china.component2())
println("--------------")
val (id,name) = china
println(id)
println(name)
为方便理解上述代码,可看编译后对应生成的java代码:
public static final void main()
Country china = new Country(0, "China");
System.out.println(china);
int id = china.component1();
System.out.println(id);
String var9 = china.component2();
System.out.println(var9);
var9 = "--------------";
System.out.println(var9);
id = china.component1();
String name = china.component2();
System.out.println(id);
System.out.println(name);
运行结果:
Country(id=0, name=China)
0
China
-------------
0
China
需要注意,kotlin数据类在没有添加allOpen和noArg插件时生成的对应的JavaBean类是final类型的且没有无参构造函数,这是kotlin存在的问题,只有在集成了allOpen和noArg插件后才会在对应生成的Java类中实现无参构造函数且为非final类,我们才能像使用普通JavaBean类那样使用我们需要的数据类,不过好像还是有坑,其成员变量还是final型的,并且没有对应的setter方法,只有getter方法,因此可能使用时还会有所限制
内部类
- 定义在类内部的类
- 与类成员有相似的访问控制
- 默认是静态内部类,非静态用inner关键字
- 使用this@Outter,this@Inner关键字来定位是外部类变量或内部类变量
匿名内部类
- 没有定义名字的内部类
- 类名编译时生成,类似Outter$1.class
- 可继承父类、实现多接口,与Java注意区别
示例代码:
class Outter
val a:Int = 4
class Inner1 //默认为静态内部类
fun getOutterA():Int
// return a //错误,编译不通,静态内部类,无法访问外部类变量
return 0
inner class Inner2 //设为非静态内部类
val a:Int = 5
fun getOutterA():Int
return this@Outter.a //可以访问外部变量a,若内部内中有同名变量,访问外部变量需用this@Outter
fun getInnerA():Int
// return this@Inner2.a //获取内部变量a,可用this@Inner2
return a //也可以直接返回内部变量名
静态内部类与非静态内部类方法调用示例代码:
fun main()
val mOutter = Outter()
println(mOutter.Inner2().getInnerA())//非静态内部类方法调用,需先new外部类实例
println(Outter.Inner1().getOutterA())//静态内部类方法调用,直接使用外部类类名
println(mOutter.Inner2().getOutterA())
枚举(实例可数)
- 实例可数的类,注意枚举也是类
- 可以修改构造,添加成员
- 可以提升代码的表现力,也有一定的性能开销
示例代码:
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 "$id->$name"
fun main()
println(LogLevel.DEBUG.getTag())
println(LogLevel.valueOf("ERROR"))
可查看其生成对应java代码:
public enum LogLevel
VERBOSE,
DEBUG,
INFO,
WARN,
ERROR,
ASSERT;
private final int id;
@NotNull
public final String getTag()
return this.id + "->" + this.name();
@NotNull
public String toString()
return this.id + "->" + this.name();
public final int getId()
return this.id;
private LogLevel(int id)
this.id = id;
// EnumUseKt.java
package com.zhusp.kotlinuse.dataclass;
public final class EnumUseKt
public static final void main()
String var0 = LogLevel.DEBUG.getTag();
System.out.println(var0);
LogLevel var1 = LogLevel.valueOf("ERROR");
System.out.println(var1);
// $FF: synthetic method
public static void main(String[] var0)
main();
运行结果:
1->DEBUG
4->ERROR
密封类(子类数量有限的类)
子类可数
- <v1.1,子类必须定义为密封类的内部类
-
=v1.1,子类只需要与密封类在同一个文件中即可
与枚举不同,枚举是其实例有限,而密封类是子类数量有限
示例代码
密封类关键字:sealed
sealed class PlayerCMD
class PlayCMD(url:String,position: Long = 0):PlayerCMD()
class SeekCMD(position:Long):PlayerCMD()
object PauseCMD:PlayerCMD()
object StopCMD:PlayerCMD()
//不可在当前文件以外定义其它PlayerCMD的子类,这么做是为了限制类的扩展,防止可能存在的安全隐患
以上是关于Kotlin学习笔记之面向对象的主要内容,如果未能解决你的问题,请参考以下文章