一、智能类型转换
在Kotlin中只要对类型进行了判断,直接可以使用父类对象调用子类中的函数,无需强制类型转换。
fun main(args: Array<String>) {
val shepHerdDog: DogNew = ShepHerdDog()
if (shepHerdDog is ShepHerdDog) {//1、类型判断
//2、类型强转
// val shepHerdDog1 = shepHerdDog as ShepHerdDog
//2、Kotlin无需强转,直接调用
shepHerdDog.herdShep()//牧羊犬放羊
}
val ruralDog: DogNew = RuralDog()
if (ruralDog is RuralDog) {
ruralDog.watchDoor()//中华田园犬看家
}
}
//狗抽象类
abstract class DogNew
// 牧羊犬
class ShepHerdDog : DogNew() {
//功能
fun herdShep() { println("牧羊犬放羊") }
}
// 中华田园犬
class RuralDog : DogNew() {
//功能
fun watchDoor() { println("中华田园犬看家") }
}
安全的类型转换: 在转换操作符后面添加一个 ?
,就不会把程序 crash
掉了,当转化失败的时候,就会返回一个 null
fun main(args: Array<String>) {
val dog: DogNew = ShepHerdDog()
val test: RuralDog? = dog as? RuralDog
println(test)//null
}
在空类型中智能类型转换
val a: String? = "123"
//正常使用空安全调用
println(a?.length)//3
if(a is String){
//判断了是String类型,在此代码块中a不可能为空,所以无需使用空安全调用
println(a.length)//3
}
二、嵌套类
Java中,将一个类定义在另一个类内部,不加任何的修饰符,那它将被默认为一个内部类,而加上static修饰,则成为了一个嵌套类。
Kotlin中,定义在另一个类内部的类没有任何修饰的情况下将被默认为嵌套类,不持有外部类的引用,如果要将它声明为一个内部类,则需要加上inner修饰符
嵌套类中不能持有外部类的引用,无法直接调用外部类中的属性
嵌套类可以直接创建实例,方式是包装类.嵌套类
fun main(args: Array<String>) {
//创建嵌套类对象
val innerClass = OutClass.InnerClass()
innerClass.sayHello()//你好
}
class OutClass {
var name: String = "张三"
/**
* 嵌套类
* 嵌套类是属于静态类 和外部类没有任何关系
*/
class InnerClass {
fun sayHello() {
println("你好")
//println("你好$name")//无法直接调用外部类 中属性
}
}
}
三、内部类
3.1、内部类定义
内部类需要增加inner
关键字修饰,内部类可以直接访问外部类属性,内部类不能直接创建实例,需要通过外部类调用
fun main(args: Array<String>) {
//创建内部类对象
val innerClass = OutClassNew().InnerClass()
innerClass.sayHello()//你好张三
}
class OutClassNew {
val name: String = "张三"
/**
* 内部类需要 增加inner 关键字
* 内部类和java的内部类是一样的 需要依赖于外部类对象的环境
*/
inner class InnerClass {
fun sayHello() {
println("你好$name")//内部类可以直接访问外部类属性
}
}
}
3.2、内部类使用this
fun main(args: Array<String>) {
//创建内部类对象
val innerClass = OutClassNew2().InnerClass()
innerClass.sayHello()//你好,张三
}
class OutClassNew2 {
val name: String = "张三"
/**
* 内部类 增加inner 关键字
* 内部类和java的内部类是一样的 需要依赖于外部类对象的环境
* this@tag和java里面的OutClassNew2.this.name一样的
*/
inner class InnerClass {
fun sayHello() {
println("你好,${this@OutClassNew2.name}")
}
}
}
3.3、匿名内部类
//使用对象表达式创建匿名内部类实例
btMainOK.setOnClickListener(object : View.OnClickListener{
override fun onClick(p0: View?) {
//代码块
}
})
//使用lambda表达式创建匿名内部类实例
btMainOK.setOnClickListener {
//代码块
}
四、泛型
4.1、泛型定义
fun main(args: Array<String>) {
//用水果箱子
val fruitBox = FruitBox(Apple("红富士苹果"))
println(fruitBox.thing)//Apple(name='红富士苹果')
//用未知箱子
val sonBox = SonBox<String>("张三")
println(sonBox.thing)//张三
}
/**
* 箱子
* 物品类型不确定 定义泛型和使用泛型
* 定义对象的时候使用泛型
*/
open class Box<T>(var thing: T)
/**
* 水果箱子
* 定义子类时候执行泛型
*/
class FruitBox(fruit: Fruit) : Box<Fruit>(fruit)
/**
* 未知箱子
* 定义子类的时候不知道具体类型,继续使用泛型
*/
class SonBox<T>(thing: T) : Box<T>(thing)
//水果抽象类
abstract class Fruit
// 苹果 实体类
class Apple(var name: String) : Fruit() {
override fun toString(): String {
return "Apple(name='$name')"
}
}
// 橘子 实体类
class Orange : Fruit()
4.2、泛型函数
fun main(args: Array<String>) {
parseType("131")//String类型
parseType(true)//未知类型
}
// 判读类型函数
fun <T> parseType(thing: T) {
when (thing) {
is String -> println("String类型")
is Int -> println("Int类型")
is Double -> println("Double类型")
else -> println("未知类型")
}
}
4.3、泛型上线
默认的上界是Any?
,是可空类型,如果确定为非空类型的话,应该使用<T: Any>
。
fun main(args: Array<String>) {
//泛型上线:限制存放的类型
// val fruitBox2 = FruitBox2<Orange2>(Orange2())
val fruitBox2 = FruitBox2<Apple2>(Apple2("红富士苹果"))
println(fruitBox2.thing)//Apple(name='红富士苹果')
}
/**
* 箱子
* 物品类型不确定 定义泛型和使用泛型
* 定义对象的时候使用泛型
*/
open class Box2<T>(var thing: T)
/**
* 水果箱子
* 定义子类的时候不知道具体类型,继续使用泛型
* T:Fruit2 泛型上限 泛型智能是Fruit2类型或者Fruit2类型的子类
*/
class FruitBox2<T : Fruit2>(fruit: T) : Box2<T>(fruit)
//水果抽象类
abstract class Fruit2
//苹果 实体类
class Apple2(var name: String) : Fruit2() {
override fun toString(): String {
return "Apple(name='$name')"
}
}
// 橘子 实体类
class Orange2 : Fruit2()
泛型约束中的尖括号中只能指定一个上界,如果需要多个上界,需要一个单独的 where
子句:
fun main(args: Array<String>) {
val listOf = listOf("1","3","2","8","0")
println(cloneWhenGreater(listOf, "1"))//[3, 2, 8]
}
//查找集合中大于指定参数的数据
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<Any>
where T : Comparable<T>,
T : CharSequence {
return list.filter { it > threshold }
}
4.4、泛型擦除
Kotlin和Java一样,Kotlin的泛型在运行时也被擦除了,这意味着实例不会携带用于创建它的类型实参的信息。解决泛型擦除方案:第一步:泛型前加reified
关键字,第二步:方法前加上inline
关键字
注意 带 reified
的内联函数不能再Java中调用,普通的 inline
内联函数可以;
fun main(args: Array<String>) {
// val box = Box<String>("wo")
// println(box.javaClass.name)//com.zp.demo.Box
// val box2 = Box<Int>(101)
// println(box2.javaClass.name)//com.zp.demo.Box
parseType2(10)
println(isA<Int>(1))//true
}
/**
* 判读类型函数
* 解决泛型擦除方案:
* 第一步:泛型前加reified关键字
* 第二步:方法前加上inline关键字
*/
inline fun <reified T> parseType2(thing: T) {
//获取传递的thing的class类型
val name = T::class.java.name
println(name)//java.lang.Integer
when (thing) {
is String -> println("String类型")
is Int -> println("Int类型")
is Double -> println("Double类型")
else -> println("未知类型")
}
}
//fun <T> isA(value: Any) = value is T // 不能确定T
inline fun <reified T> isA(value: Any) = value is T // 解决泛型不被擦除
4.5、泛型类型投射
Java中只读类型使用上界通配符? extends T
,只写类型使用下界通配符? super T
。Kotlin 中提供了类似功能的两个操作符out
和in
fun main(args: Array<String>) {
// val arrayList = ArrayList<AA>()
// setThingList(arrayList)//AA是Thing子类, in Thing 不能被接收
val arrayList = ArrayList<Thing>()
setThingList(arrayList)//0 Thing是Thing本身 in Thing 可以接收
val arrayListObj = ArrayList<Obj>()
setThingList(arrayListObj)//0 Obj 是Thing父类 in Thing 可以接收
val arrayListAA = ArrayList<AA>()
setThingList2(arrayListAA)//0 AA是Thing子类 out Thing 可以接收
val arrayList2 = ArrayList<Thing>()
setThingList2(arrayList2)//0 Thing是Thing本身 out Thing 可以接收
// val arrayListObj2 = ArrayList<Obj>()
// setThingList2(arrayListObj2)//Obj 是Thing父类 out Thing 不可以接收
}
// in:接收当前类型或者它的父类 相当于java的 ? super
fun setThingList(list: ArrayList<in Thing>) {// in Thing 接收只能是Thing或Thing父类
println(list.size)
}
// out:接收当前类型或它的子类 相当于java的 ? extents
fun setThingList2(list: ArrayList<out Thing>) {// in Thing 接收只能是Thing或者Thing子类
println(list.size)
}
// 抽象类
abstract class Obj
// 实体类Thing
open class Thing : Obj()
// 实体A
class AA(var name: String) : Thing() {
override fun toString(): String {
return "BB(name='$name')"
}
}
4.6、星号投射
fun main(args: Array<String>) {
val arrayListInt = ArrayList<Int>()
setFruitList3(arrayListInt)//0
val arrayListBoolean = ArrayList<Boolean>()
setFruitList3(arrayListBoolean)//0
}
// * 可以传递任何类型 相当于java的 ?
fun setFruitList3(list: ArrayList<*>) {
println(list.size)
}
五、中缀表达式
中缀表达式使用条件:
- 非顶层函数
- 接收参数为1个
- 函数前加上
infix
关键字
fun main(args: Array<String>) {
// Kotlin中二元元组的中缀表达式
//普通方式定义二元元组
val str = Pair<String, String>("1", "2")
println(str.first)//1
println(str.second)//2
//使用中缀表达式定义二元元组
val str1="1" to "2"
println(str1.first)//1
println(str1.second)//2
//自定义 中缀表达式
val person = Person()
person sayHello "张三" //你好张三
}
class Person {
/**
* 中缀表达式使用条件:
* 非顶层函数
* 接收参数为1个
* 函数前加上 infix 关键字
*/
infix fun sayHello(name: String) {
println("你好$name")
}
}
六、类委托及属性委托
委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by
实现委托。
6.1、类委托
类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
fun main(args: Array<String>) {
val smallHeadFather = SmallHeadFather()
smallHeadFather.wash()//大头儿子开始洗碗
}
//洗碗能力接口WashPower
interface WashPower {
fun wash()
}
//大头儿子BigHeadSon
class BigHeadSon : WashPower {
override fun wash() {
println("大头儿子开始洗碗")
}
}
/**
* 小头爸爸SmallHeadFather
* 类委托: 使用 by 关键字 将行为能力委托给其他类
* 小头爸爸将洗碗委托给大头儿子
*/
class SmallHeadFather : WashPower by BigHeadSon()
6.2、类委托实现二
委托时传入委托类,将行为委托给传入的委托类来执行
fun main(args: Array<String>) {
//创建小头爸爸 并指定大头儿子洗碗(类委托)
val smallHeadFather2 = SmallHeadFather2(BigHeadSon2())
smallHeadFather2.wash()//大头儿子开始洗碗
}
//洗碗能力接口WashPower
interface WashPower2 {
fun wash()
}
//大头儿子BigHeadSon
class BigHeadSon2 : WashPower2 {
override fun wash() {
println("大头儿子开始洗碗")
}
}
/**
* 小头爸爸SmallHeadFather
* 类委托: 使用 by 关键字 将行为能力委托给其他类(当创建时需要传委托类)
* 小头爸爸将洗碗委托给别人(创建小头爸爸时指定)
*/
class SmallHeadFather2(val washPower2: WashPower2) : WashPower2 by washPower2
6.3、类委托功能加强
当使用类委托时可以监听委托能力执行流程,可在执行前后作出相应的处理
fun main(args: Array<String>) {
//创建小头爸爸 并指定大头儿子洗碗(类委托)
val smallHeadFather3 = SmallHeadFather3(BigHeadSon3())
smallHeadFather3.wash()
//打印结果:
// 付1元前给大头儿子
// 大头儿子开始洗碗
// 洗完了 再接再厉
}
//洗碗能力接口WashPower
interface WashPower3 {
fun wash()
}
//大头儿子BigHeadSon
class BigHeadSon3 : WashPower3 {
override fun wash() {
println("大头儿子开始洗碗")
}
}
/**
* 小头爸爸SmallHeadFather
* 类委托: 使用 by 关键字 将行为能力委托给其他类(当创建时需要传委托类)
* 小头爸爸将洗碗委托给别人(创建小头爸爸时指定) 小头爸爸在让大头儿子洗碗之前先付钱,洗完后 夸一下大头儿子
*/
class SmallHeadFather3( val washPower3: WashPower3) : WashPower3 by washPower3 {
override fun wash() {
println("付1元前给大头儿子")
washPower3.wash()
println("洗完了 再接再厉")
}
}
6.4、属性委托
属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。
fun main(args: Array<String>) {
//实现一
val boy = Boy()
boy.moeny=100.0
println(boy.moeny)//50.0
//实现二
val mother2 = Mother2()
val boy2 = Boy2(mother2)
boy2.moeny=180.0
println(mother2.myMoeny)//90.0
println(boy2.moeny)//90.0
}
/*-------------------------------属性委托实现一-----------------*/
//孩子类
class Boy {
//属性委托 使用 by关键字
var moeny: Double by Mother()
}
//妈妈类
class Mother{
//儿子金库
var sonMoeny:Double=0.0
//妈妈自己金库
var myMoeny:Double=0.0
// 属性委托实现 getValue 取值方法
operator fun getValue(boy: Boy, property: KProperty<*>): Double {
return sonMoeny
}
// 属性委托实现 setValue 设置值方法
operator fun setValue(boy: Boy, property: KProperty<*>, d: Double) {
val d1 = d / 2
//妈妈金库
myMoeny=d1
//儿子金库
sonMoeny=d-d1
}
}
/*-------------------------------属性委托实现二----------------*/
// 孩子2类
class Boy2(val mother2: Mother2) {
// 属性委托 使用 by关键字 委托类是待创建Boy2时指定
var moeny: Double by mother2
}
// 妈妈2类
class Mother2{
//儿子金库
var sonMoeny:Double=0.0
//妈妈自己金库
var myMoeny:Double=0.0
// 属性委托实现 getValue 取值方法
operator fun getValue(boy: Boy2, property: KProperty<*>): Double {
return sonMoeny
}
// 属性委托实现 setValue 设置值方法
operator fun setValue(boy: Boy2, property: KProperty<*>, d: Double) {
val d1 = d / 2
//妈妈金库
myMoeny=d1
//儿子金库
sonMoeny=d-d1
}
}
七、惰性加载
惰性加载是一种常见的模式,需要使用by lazy
关键字,by lazy
变量必须通过val
修饰,当第一次使用到的时候才初始化加载并赋值,当初始化过程消耗大量资源并且在使用对象时并不总是需要数据时,惰性加载非常有用,减少内存开销.
fun main(args: Array<String>) {
val per = Per()
println(per.name)
//打印结果:
// 初始化了
// 张三
println(per.name1)//李四
}
class Per {
/**
* 惰性加载:用的时候再赋值
* 字段:val 不可变
* by lazy 放到成员变量中 可以单独存在
* by lazy 返回值就是最后一行
* by lazy 线程安全 (同步)
*/
val name: String by lazy {
println("初始化了")
"张三"
}
val name1: String = "李四"
}
八、延迟加载
延迟加载使用lateinit
关键字来修饰,lateinit
需要通过var
修饰,延迟加载开始初始值为null
,到使用到时才赋上具体的值.
fun main(args: Array<String>) {
val a = Per2()
//延时加载用时必须赋值。
a.name = "张三"
println(a.name)//"张三"
}
class Per2 {
// 延迟加载: 后面可能用的时候才会赋值 定义时不知道具体是什么值,使用lateinit var 修饰字段
lateinit var name: String
var name1: String = "李四"
}
/**
* 1.by lazy 和lateinit 都可以单独使用或者放到成员变量中使用
* 2.by lazy 知道具体值 用的时候再加载
* 3.lateinit 不知道具体值 后面再赋值
* 4.by lazy变量必须通过val修饰 lateinit需要通过var修饰
*/
九、扩展函数
Kotlin中的扩展函数,相当于Java中的静态方法。
扩展函数并不是真正地修改了原来的类,它的这些作用效果是以静态导入的方式来实现的。扩展函数可以被声明在任何文件中,因此有个通用的方式是把一系列有关的函数放在一个新建的文件里,就像我们刚才所说的工具类当中。
fun main(args: Array<String>) {
val str: String? = null
println(str.myIsEmpty())//true
val father = Father()
father.haha()
father.sayHello()//张三,打招呼
}
/**
* String 扩展函数 判断是否为空
* 扩展函数可以访问当前对象里面的字段和方法
*/
fun String?.myIsEmpty(): Boolean {
return this == null || this.length == 0
}
/**
* Father 扩展函数
* 扩展函数可以访问当前对象里面的字段和方法
*/
fun Father.sayHello() {
println("$name,打招呼")
}
class Father {
var name: String = "张三"
fun haha() {
}
}
十、伴生对象
Java中有static
关键字表示静态成属性和方法,但在Kotlin中没有static
关键字,所以伴生对象和顶层函数一起来弥补了这一缺憾,伴生对象companion object
相当于Java中static
关键。
fun main(args: Array<String>) {
//伴生对象直接使用对象.访问
println(Per3.age)//18
val per3 = Per3()
//非伴生对象访问
println(per3.name)//张三
}
class Per3 {
var name: String = "张三"
/**
* 伴生对象
* 作用:就是控制属性的静态
*/
companion object {
var age: Int = 18
}
}
十一、kotlin中单例
11.1、kotlin的object单例
Kotlin中使用object
修饰类所有的字段都是static
静态的, 方法不是。
fun main(args: Array<String>) {
//单例调用属性
println(Utils.name)//张三
//单例调用方法
Utils.sayHello()
}
/**
* object单例 使用object关键字修饰
* 所有的字段都是static静态 方法不是
* object试用条件:字段不多的时候
* java可以控制字段是静态还是非静态 static
* kotlin没有static关键字
*/
object Utils {
var name: String = "张三"
fun sayHello() {
}
}
上方代码反编译为Java代码如下:
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String var1 = Utils.INSTANCE.getName();
System.out.println(var1);
Utils.INSTANCE.sayHello();
}
public final class Utils {
@NotNull
private static String name;
public static final Utils INSTANCE;
@NotNull
public final String getName() {
return name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
name = var1;
}
public final void sayHello() {
}
static {
Utils var0 = new Utils();
INSTANCE = var0;
name = "张三";
}
}
11.2、Kotlin和java一样的单例
使用companion object
伴生对象来控制静态属性,使用by lazy
惰性加载来创建实例
fun main(args: Array<String>) {
val name = Util.instant.name
println(name)//张三
}
class Util private constructor() {
//1、private constructor() 私有构造
//非静态
var name: String = "张三"
//2、使用伴生对象控制静态属性
companion object {
//静态
var age = 18
//instance代表Util的对象实例
val instant by lazy { Util() }//3、使用惰性加载创建实例 只会加载一次 线程安全
}
}
十二、枚举
12.1、枚举定义
定义枚举类:在class类前加enum
声明
fun main(args: Array<String>) {
todo(WEEK.星期二)//上班
}
fun todo(week: WEEK) {
when (week) {
WEEK.星期一, WEEK.星期二, WEEK.星期三, WEEK.星期四, WEEK.星期五 -> println("上班")
// in WEEK.星期一..WEEK.星期五 -> println("上班")//区间可以使用,前提写枚举循序写对(星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日)
WEEK.星期六, WEEK.星期日 -> println("休息")
else -> println("未知")
}
}
//一周的枚举
enum class WEEK {
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
12.2、枚举加强
枚举中可以定义构造函数。
fun main(args: Array<String>) {
println(COLOR.RED.r)//255
println(COLOR.RED.g)//0
println(COLOR.RED.b)//0
}
/**
* 颜色枚举
* 枚举里面可以定义构造函数
*/
enum class COLOR(var r: Int, var g: Int, var b: Int) {
RED(255, 0, 0), BLUE(0, 0, 255), GREEN(0, 255, 0)
}
枚举中还支持以声明自己的匿名类及相应的方法、以及覆盖基类的方法,如果枚举类定义任何成员,要使用分号将成员定义中的枚举常量定义分隔开。
println( ProtocolState.TALKING.signal().toString())//WAITING
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
十三、数据类
使用data修饰的类为数据类,编译器自动从在主构造函数定义的全部特性中得到以下成员:
- get set
- equals()/hashCode()
- toString()
- componentN() 方法对应按声明顺序出现的所有属性
- copy() 方法
如果有某个函数被明确地定义在类里或者被继承,编译器就不会生成这个函数。
数据类的定义具有以下几个特点:
- 类由关键字
data
修饰 - 类的构造参数必须由
var
/val
修饰,否则编译不通过 - 编译器会自动生成一些常用方法,你也可以自定义它们,自定义后,编译器就不会生成这个函数。
- 和普通类一样,如果你需要一个无参构造方法,可以将构造方法的参数都设置默认值
fun main(args: Array<String>) {
//构建
val news = News("标题", "简介", "路径", "内容")
println(news.title)//标题
println(news.desc)//简介
//第一个参数
println(news.component1())//标题
//第二个参数
println(news.component2())//简介
//解构
val (title, desc, imgPath, content) = News("标题", "简介", "路径", "内容")
println(title)//标题
println(desc)//简介
println(imgPath)//路径
println(content)//内容
}
/**
* 数据类 使用data 修饰
* 默认有 get set、equals()/hashCode()、toString() 、componentN() 、copy() 方法
*/
data class News(var title: String, var desc: String, var imgPath: String, var content: String)
十四、密封类
-
密封类用
sealed
关键词表示 -
密封类的子类只能定义在密封类的内部或同一个文件中,因为其构造方法为私有的
-
密封类相比于普通的open类,可以不被此文件外被继承,有效保护代码
-
与枚举的区别:密封类适用于子类可数的情况,枚举适用于实例可数的情况
-
密封类的好处在于:使用when表达式,如果能覆盖所有情况,就无需再添加else子句
fun main(args: Array<String>) {
println(hasRight(JonSnow()))//false
println(hasRight(NedStark.RobStark()))//true
}
/**
* 判断有没有继承权
* 先把所有有继承权的(数量固定) 放在一起 其他都不用管了
*/
fun hasRight(startk: NedStark): Boolean {
return when (startk) {
is NedStark.AryaStark -> true
is NedStark.SansaStark -> true
is NedStark.RobStark -> true
is NedStark.BrandonStark -> true
else -> false//如果上方条件覆盖了所有,将不用写else
}
}
/**
* 密封类 使用 sealed 修饰
* 密封类封装的是类型 类型是确定的
*/
sealed class NedStark {
class RobStark : NedStark()
class SansaStark : NedStark()
class AryaStark : NedStark()
class BrandonStark : NedStark()
}
class JonSnow : NedStark()
class JonSnow2 : NedStark()
class JonSnow3 : NedStark()
class JonSnow4 : NedStark()
十五、运算符重载
15.1、运算符重载案例
使用运算符重载将两个男孩对象相加得出两个男孩年龄的和。
/** kotlin里面每一个运算符对应的都是一个方法 运算符相当于是方法的简写(例如"+"-->plus)
* 第一步:找到对应的函数
* 第二步:函数前加上operator
*/
val a = Boy()
val boy = a + 1
println("男孩年龄:${boy.age}")//男孩年龄:17
val b = Boy()
val i = a + b
println("两个男孩年龄和:$i")//两个男孩年龄和:33
// 男孩
class Boy {
//静态属性
var name: String = "李四"
var age: Int = 16
//动态行为
fun shopping() {
println("去购物")
}
// + 定义对应的运算符函数
operator fun plus(age: Int): Boy {
this.age += age
return this
}
// + 定义对应的运算符函数
operator fun plus(b: Boy): Int {
return this.age + b.age
}
}
15.2、Kotlin中表达式翻译表
表中左边为使用形态,右边为其执行形态。
表达式 | 翻译为 |
---|---|
a+b | a.plus(b) |
a-b | a.minus(b) |
a*b | a.times(b) |
a/b | a.div(b) |
a%b | a.rem(b) |
a…b | a.rangeTo(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, ……, i_n] | a.get(i_1, ……, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, ……, i_n] =b | a.set(i_1, ……, i_n,b) |
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i,j) | a.invoke(i, j) |
a(i_1,……,i_n) | a.invoke(i_1, ……,i_n) |
a +=b | a.plusAssign(b) |
a -=b | a.minusAssign(b) |
a *=b | a.timesAssign(b) |
a /=b | a.divAssign(b) |
a %=b | a.modAssign(b) |
a ==b | a?.equals(b) ?: (b === null) |
a !=b | !(a?.equals(b) ?: (b ===null)) |
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >=b | a.compareTo(b) >=0 |
a <=b | a.compareTo(b) <=0 |
注意:===
和 !==
(同一性检查)不可重载,因此不存在对他们的约定。