kotlin 实战之面向对象特性全方位总结

class TestClass constructor(name: String, age: Int) {

private val name = name.toLowerCase()

//初始化代码块中可以直接使用构造方法的参数,给类对象属性赋初值

init {

println(age)

}

}

//私有构造方法的类定义

class TestClass1 private constructor(name: String)

//如果 primary 构造方法没有任何注解或是可见性关键字修饰,则 constructor 关键字可以省略

class TestClass2 (name: String)

多个构造方法声明的写法案例(包含代码执行顺序):

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

class Person constructor(name: String) {

private var name: String

private var age: Int

private var address: String

init {

println(name) //name 是 constructor 构造方法的参数

this.name = name

this.age = 16

this.address = “zhuhai”

}

//secondary 构造方法必须要直接或者间接调用其 primary 构造方法

constructor(name: String, age: Int): this(name) {

println(“$name, $age”)

this.name = name

this.age = age

this.address = “hanzhong”

}

//secondary 构造方法间接调用其 primary 构造方法

constructor(name: String, age: Int, address: String): this(name, age) {

this.address = address

}

fun print() {

println(“name: ${this.name}, age: $age, address: $address”)

}

}

/**

运行输出值为:

ruoshui

gongjiang

gongjiang, 18

android

android, 19

name: ruoshui, age: 16, address: zhuhai

name: gongjiang, age: 18, address: hanzhong

name: android, age: 19, address: guangdong

*/

fun testRun() {

var person0 = Person(“ruoshui”)

var person1 = Person(“gongjiang”, 18)

var person2 = Person(“android”, 19, “guangdong”)

person0.print()

person1.print()

person2.print()

}

kotlin 类语法糖简写方式对上面案例的简写实现:

//简写:直接构造方法里声明类的成员变量属性

class Person1 (private val name: String,

private var age: Int,

private val address: String) {

fun print() {

println(“name: ${this.name}, age: $age, address: $address”)

}

}

/**

运行输出值为:

name: yan, age: 18, address: guangdong

*/

fun testRun() {

var person = Person1(“yan”, 18, “guangdong”)

person.print()

}

kotlin 的构造方法可以具备默认参数值,这点和 java 很不一样。如果 primary 构造方法的所有参数都有默认值,则编译器还会生成一个额外新的无参数构造方法,并且使用这个构造方法的默认值。具体用法类似如下:

//如果 primary 构造方法的所有参数都有默认值,则编译器还会生成一个额外新的无参数构造方法,并且使用这个构造方法的默认值

class Person2 (private val name: String = “ruoshui”,

private var age: Int = 20,

private var address: String = “zhuhai”) {

fun print() {

println(“name: ${this.name}, age: $age, address: $address”)

}

}

class Person3 (private val name: String = “ruoshui”,

private var age: Int) {

fun print() {

println(“name: ${this.name}, age: $age”)

}

}

/**

运行输出值为:

name: ruoshui, age: 20, address: zhuhai

name: ruoshui, age: 12

*/

fun testRun() {

var person = Person2()

person.print()

//因为 primary 构造方法参数默认值不全,所以不能使用无参构造

var person1 = Person3(age = 12)

person1.print()

}

继承与重写特性


与 java 类默认修饰符不同的另一点是,kotlin 中所有类默认都是无法被继承的,即 kotlin 中所有类默认都是 final 的。如下案例展示了类的继承:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

//默认 class 是 final 的,需要显式添加 open 以便可以被继承

open class Anim (name: String, age: Int)

//kotlin 通过 : 实现 java 的 extends 等关键字

class Dog (name: String, age: Int): Anim(name, age)

//kotlin 中如果一个类没有 primary 构造方法,

//则这个类的每个 secondary 构造方法就需要通过 super 关键字来初始化父类型,

//或是通过其他 secondary 构造方法完成这个任务。

//不同的 secondary 构造方法可以调用父类型不同的构造方法。

open class Anim1 (name: String) {

init {

println(“Anim1–$name”)

}

}

class Dog1 : Anim1 {

constructor(name: String): super(name) {

println(“dog1-0–$name”)

}

constructor(name: String, age: Int): this(name) {

println(“dog1-2–$name”)

}

}

/**

运行输出值为:

Anim1–666

dog1-0–666

dog1-2–666

*/

fun testRun() {

var anim = Dog1(“666”, 66)

}

接着我们再来看看 kotlin 中类方法的重写,这点与 java 也很不相同。kotlin 中如果一个方法想被重写,则必须显式指定 open 关键字,否则无法编译通过;子类中重写方法必须显示指定 override 关键字,否则无法编译通过。案例如下:

open class Developer {

//kotlin 中如果一个方法想被重写,则必须显式指定 open 关键字,否则无法编译通过

open fun skill() {

println(“developer”)

}

//子类默认具备该能力,但无法重写

fun money() {

println(“666”)

}

//可以被子类继承

open fun wife() {

println(“haha”)

}

}

open class Android: Developer() {

//kotlin 中重写方法必须显示指定 override 关键字,否则无法编译通过

override fun skill() {

println(“android”)

}

//Android 类的 wife 方法重写了父类,但是无法被子类重写,因为声明了 final

final override fun wife() {

super.wife()

println(“green”)

}

}

/**

运行输出值为:

android

666

haha

green

*/

fun testRun() {

var dev = Android()

dev.skill()

dev.money()

dev.wife()

}

看完类方法的重写,我们再来看看 kotlin 的类属性重写。val 的属性可以被重写成 var,var 的属性不能被重写成 val。因为一个 val 属性相当于一个 get 方法,var 相当于一个 get 和 set 方法,所以增加范围是可以的,但是缩小范围是不行的(一旦缩小如果父类中有对 var 的 set 操作则没重写方法的情况下就会异常,所以缩小范围不被允许)。如下是使用案例:

open class PaPaPa {

open val name: String = “pa”

open fun print() {

println(“name= n a m e , t h i s . n a m e = name, this.name= name,this.name={this.name}”)

}

}

open class XoXoXo: PaPaPa() {

override val name: String = “xo”

//val 只读属性默认只有 get 方法

open val address: String get() = “beijing”

}

//属性重写简写方式

class NcNcNc (override var name: String = “nc”): XoXoXo() {

override fun print() {

super.print()

println(“address is $address”)

}

//重写父类属性并调用父类属性

override val address: String

get() = “6666” + super.address

}

/**

运行输出值为:

name=nc, this.name=nc

address is 6666beijing

*/

fun testRun() {

var oo = NcNcNc()

oo.print()

}

和 java 一样,类的继承有一个特殊的,那就是接口,kotlin 中接口的实现和 java 比较类似,如下案例:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

interface Nb {

//接口声明

fun test1()

//接口可以既有声明也可以有实现

fun test() {

println(“nb”)

}

}

class ChildNb: Nb {

override fun test1() {

println(“test1”)

}

override fun test() {

println(“test”)

super.test()

}

}

kotlin 为我们提供了一种混用冲突的解决方案,当继承的类和接口拥有相同签名方法时子类必须重写父类相同签名方法,否则无法编译通过,此外需要显式通过<>语法指定使用哪个基类的方法。案例如下:

interface Nb {

fun test() {

println(“nb”)

}

}

open class Tt {

open fun test() {

println(“tt”)

}

}

class ChildNb: Nb, Tt() {

//当继承的类和接口拥有相同签名方法时子类必须重写父类相同签名方法,否则无法编译通过

//此外需要显式通过<>指定使用哪个基类的方法

override fun test() {

super.test()

println(“test”)

super.test()

}

}

/**

运行输出值为:

tt

test

nb

*/

fun testRun() {

var oo = ChildNb()

oo.test()

}

对于抽象类的声明与继承基本和 java 没啥区别,具体案例如下:

open class Base {

open fun method() {

println(“base”)

}

}

abstract class Child: Base() {

//父类的实现方法可以被抽象子类重写成抽象方法供当前抽象类的子类实现

override abstract fun method()

}

class Child1: Child() {

override fun method() {

println(“child1”)

}

}

对象声明及伴生对象


kotlin 的对象声明你可以理解成一种单例的语言层面支持实现能力。定义一个类用 class,声明一个对象用 object,全局声明的对象就是一个对象实例,且全局唯一。案例如下:

//定义了一个名为 TestObject 的对象实例

object TestObject {

fun method() {

println(“666”)

}

}

/**

运行输出值为:

666

*/

fun testRun() {

TestObject.method()

}

kotlin 的伴生对象相对于 java 来说也是一种新的特性,本质来说,kotlin 与 java 不同的是 kotlin 的类没有 static 方法。在大多数情况下 kotlin 推荐的做法是使用包级别的函数来充当静态方法角色,kotlin 会将包级别的函数当作静态方法看待。而 kotlin 中一个类最多只能有一个伴生对象,这个伴生对象也类似 java 的 static 成员。案例如下:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

class Yan {

//TestObj 名字可以省略,编译器默认名字是 Companion

//一个类中最多只能有一个伴生对象

companion object TestObj {

var name: String = “666”

fun method() = println(“${this.name} – method”)

}

}

/**

运行输出值为:

666 – method

777 – method

class cn.yan.test.Yan$TestObj

*/

fun testRun() {

Yan.TestObj.method()

//简写,kotlin 语法糖,没有 @jvmStatic 情况下本质还是转为了 Yan.TestObj 静态成员调用方式

Yan.name = “777”

Yan.method()

println(Yan.TestObj.javaClass)

}

虽然伴生对象的成员看起来像是 java 的静态成员,但在运行期他们依旧是真实对象的实例成员。在 jvm 实现上可以将伴生对象的成员真正生成为类的静态方法与属性,具体通过@JvmStatic注解实现。

伴生对象的本质原理是在编译后生成一个静态内部类来实现的,我们对上面的案例代码进行反编译(javap)结果如下:

//反编译的 cn.yan.test.Yan 类

yandeMacBook-Pro:test yan$ javap -c Yan.class

Compiled from “Test2.kt”

public final class cn.yan.test.Yan {

//Yan 这个 kotlin 类中的伴生对象名生成了一个静态成员变量,名字为 TestObj

public static final cn.yan.test.Yan$TestObj TestObj;

//Yan 这个 kotlin 类的构造函数

public cn.yan.test.Yan();

Code:

0: aload_0

1: invokespecial #8 // Method java/lang/Object.“”😦)V

4: return

//Yan 这个 kotlin 类的 static 代码块实例化了Yan$TestObj静态内部类并赋值给当前类的静态成员属性TestObj

static {};

Code:

0: new #38 // class cn/yan/test/Yan$TestObj

3: dup

4: aconst_null

5: invokespecial #41 // Method cn/yan/test/Yan$TestObj.“”:(Lkotlin/jvm/internal/DefaultConstructorMarker;)V

8: putstatic #43 // Field TestObj:Lcn/yan/test/Yan$TestObj;

11: ldc #45 // String 666

13: putstatic #20 // Field name:Ljava/lang/String;

16: return

//在 Yan 这个类中新增了伴生对象里面属性 var name 的 get 操作

public static final java.lang.String access g e t N a m e getName getNamecp();

Code:

0: getstatic #20 // Field name:Ljava/lang/String;

3: areturn

//在 Yan 这个类中新增了伴生对象里面属性 var name 的 set 操作

public static final void access s e t N a m e setName setNamecp(java.lang.String);

Code:

0: aload_0

1: putstatic #20 // Field name:Ljava/lang/String;

4: return

}

如下是 Yan 这个 kotlin 类中伴生对象生成的静态内部类反编译代码:

//反编译的 cn.yan.test.Yan 类中伴生对象生成的静态内部类 Yan$TestObj

yandeMacBook-Pro:test yan$ javap -c Yan$TestObj.class

Compiled from “Test2.kt”

public final class cn.yan.test.Yan$TestObj {

//伴生对象内部属性 var name 的 get 方法

public final java.lang.String getName();

Code:

0: invokestatic #12 // Method cn/yan/test/Yan.access g e t N a m e getName getNamecp:()Ljava/lang/String;

3: areturn

//伴生对象内部属性 var name 的 set 方法

public final void setName(java.lang.String);

Code:

0: aload_1

1: ldc #18 // String <set-?>

3: invokestatic #24 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V

6: aload_1

7: invokestatic #27 // Method cn/yan/test/Yan.access s e t N a m e setName setNamecp:(Ljava/lang/String;)V

10: return

//伴生对象内部定义的方法

public final void method();

Code:

0: new #32 // class java/lang/StringBuilder

3: dup

4: invokespecial #35 // Method java/lang/StringBuilder.“”😦)V

7: aload_0

8: checkcast #2 // class cn/yan/test/Yan$TestObj

11: invokevirtual #37 // Method getName:()Ljava/lang/String;

14: invokevirtual #41 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

17: ldc #43 // String – method

19: invokevirtual #41 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

22: invokevirtual #46 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

25: astore_1

26: iconst_0

27: istore_2

28: getstatic #52 // Field java/lang/System.out:Ljava/io/PrintStream;

31: aload_1

32: invokevirtual #58 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V

35: return

//伴生对象生成的静态内部类的构造方法

public cn.yan.test.Yan$TestObj(kotlin.jvm.internal.DefaultConstructorMarker);

Code:

0: aload_0

1: invokespecial #61 // Method “”😦)V

4: return

}

接着我们将上面 kotlin 案例代码添加@JvmStatic注解,代码如下:

class Yan {

companion object TestObj {

@JvmStatic

var name: String = “666”

@JvmStatic

fun method() = println(“${this.name} – method”)

}

}

上面代码反编译结果如下:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

yandeMacBook-Pro:test yan$ javap -c Yan.class

Compiled from “Test2.kt”

public final class cn.yan.test.Yan {

//与上面没注解类似,静态属性

public static final cn.yan.test.Yan$TestObj TestObj;

//与上面没注解类似,构造方法

public cn.yan.test.Yan();

Code:

//与上面没注解类似,静态代码块并对静态成员赋值

static {};

Code:

//与上面没注解类似,内部指令 access 方法

public static final java.lang.String access g e t N a m e getName getNamecp();

Code:

//与上面没注解类似,内部指令 access 方法

public static final void access s e t N a m e setName setNamecp(java.lang.String);

Code:

//添加@JvmStatic后在伴生对象依赖的 Yan 类中添加的 static 属性 get 方法

public static final java.lang.String getName();

Code:

0: getstatic #39 // Field TestObj:Lcn/yan/test/Yan$TestObj;

3: getstatic #20 // Field name:Ljava/lang/String;

6: areturn

//添加@JvmStatic后在伴生对象依赖的 Yan 类中添加的 static 属性 set 方法

public static final void setName(java.lang.String);

Code:

0: getstatic #39 // Field TestObj:Lcn/yan/test/Yan$TestObj;

3: aload_0

4: putstatic #20 // Field name:Ljava/lang/String;

7: return

//添加@JvmStatic后在伴生对象依赖的 Yan 类中添加的 static 方法的调用

public static final void method();

Code:

0: getstatic #39 // Field TestObj:Lcn/yan/test/Yan$TestObj;

3: invokevirtual #46 // Method cn/yan/test/Yan$TestObj.method:()V

6: return

}

如下是 Yan 这个 kotlin 类中伴生对象生成的静态内部类反编译代码:

yandeMacBook-Pro:test yan$ javap -c Yan$TestObj.class

Compiled from “Test2.kt”

public final class cn.yan.test.Yan$TestObj {

//与上面不加注解生成的基本没区别,所以不再列出

}

通过上面例子可以看出,对于伴生对象成员加不加@JvmStatic注解对使用者没有任何区别,只对编译后生成的字节码有区别,区别如上分析,但是一定要记住,伴生对象的本质是通过静态内部类和静态成员实现的。

对象表达式与对象声明、伴生对象的区别:

  • 对象表达式是立刻初始化或者执行的。

  • 对象声明是延迟初始化的,在首次访问的时候进行。

  • 伴生对象是在其所对应的类被加载时初始化,对应 java 的静态初始化。

属性及延迟初始化


kotlin 中类成员属性的定义与 java 类似,如下案例:

class Do(name: String, address: String) {

//定义只读属性

val name: String

get() = “gongjiang”

//定义可读可写属性

var address: String = address

get() {

println(“get address”)

return field

}

set(value) {

println(“set address”)

//这里不能用 this.address = value,否则死循环

field = value

}

}

/**

运行结果:

gongjiang

set address

get address

hanzhong

*/

fun testRun() {

val dx = Do(“yb”, “zhuhai”)

//本质调用的 name 属性的 get 方法

println(dx.name)

dx.address = “hanzhong”

println(dx.address)

}

我们对上面 Do 类进行反编译,结果如下:

yandeMacBook-Pro:test yan$ javap Do.class

Compiled from “Test2.kt”

public final class cn.yan.test.Do {

//val 定义的只读属性 name 只生成了 public 的 get 方法

public final java.lang.String getName();

//val 定义的读写属性 address 生成了 public 的 get、set 方法

public final java.lang.String getAddress();

public final void setAddress(java.lang.String);

//生成了构造方法

public cn.yan.test.Do(java.lang.String, java.lang.String);

}

kotlin 要求不可空类型的属性必须要在构造方法中进行初始化,有时这种要求不太方便,譬如对于依赖注入方式(Spring、Room 等),这时可以通过 lateinit 关键字标示属性为延迟初始化,延迟初始化需要满足三个条件:

  • lateinit 只能用在类体中声明的 var 属性上,不能用在 primary 构造方法声明的属性。

  • 属性不能拥有自定义的 get、set 方法。

  • 属性类型要为非空且不能是原生类型(譬如 Int)。

如下是属性延迟初始化的案例:

class Qq {

//延迟初始化,不添加就必须初始化或者申明为 String? 类型

lateinit var name: String

//无法编译通过:Property must be initialized or be abstract

//var age: Int

//无法编译通过:‘lateinit’ modifier is allowed only on mutable properties

//lateinit val address: String

//无法编译通过:‘lateinit’ modifier is not allowed on properties of primitive types

//lateinit var count: Int

fun print() {

println(name)

}

}

/**

运行结果直接崩溃:

Exception in thread “main” kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized

at cn.yan.test.Qq.print(Test2.kt:324)

at cn.yan.test.Test2Kt.testRun(Test2.kt:337)

at cn.yan.test.Test2Kt.main(Test2.kt:22)

at cn.yan.test.Test2Kt.main(Test2.kt)

*/

fun testRun() {

val qq = Qq()

qq.print()

}

可见性及 kotlin 扩展


kotlin 提供了四种可见性修饰符:private、protected、internal、public。对于 kotlin 可见性修饰符默认不写就是 public。

kotlin 文件中顶层非类内部的方法或者属性或者类添加 private 修饰符则使用范围为当前文件。

internal 表示只能在同一个模块下使用,java 是没有模块的概念,kotlin 的模块是指在最后编译时编译到一起的一个模块。

protected 修饰符不能用在顶层类或者函数、属性中。可以用在非顶层申明中,范围为子类或者当前类。

局部变量是没有访问修饰符的概念。

kotlin 的扩展相对于 java 来说是不可思议的概念,如果我们已经定义了一个类,现在想给这个类添加额外的功能,用 java 方式解决的办法只有继承或者装饰者实现,而对于 kotlin 来说,扩展可以轻松做到这一点。如下是一个扩展案例:

//Test2.kt 文件

//假设这个类不是自己写的或者是存在于 jar 中的类

class Xunit {

var count: Int = 0

fun add(a: Int, b: Int) = a + b

fun sub(a: Int, b: Int) = a - b

fun printCount() = println(“count=$count”)

}

//kotlin 扩展给 Xunit 增加方法,背后本质中 multi 方法不会被编译插入到 Xunit 类中

fun Xunit.multi(a: Int, b: Int) = a * b

fun Xunit.doCount(a: Int) {

this.count = a

}

/**

运行结果:

2

1

4

count=12

*/

fun testRun() {

val xunit = Xunit()

println(xunit.add(1, 1))

println(xunit.sub(2, 1))

//调用类的扩展函数,本质是一种语法糖

println(xunit.multi(2, 2))

xunit.doCount(12)

xunit.printCount()

}

上面的扩展代码实现是不是很神奇,为了探究扩展的实现原理,我们需要明确的是,扩展本身不会真正的修改目标类,方法不会被编译插入到 Xunit 类中,而是在其定义的类中。可以看上面案例的反编译字节码得知:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

yandeMacBook-Pro:test yan$ javap Xunit.class

Compiled from “Test2.kt”

//Xunit 类中没有我们扩展的属性和方法

public final class cn.yan.test.Xunit {

public final int getCount();

public final void setCount(int);

public final int add(int, int);

public final int sub(int, int);

public final void printCount();

public cn.yan.test.Xunit();

}

扩展方法代码反编译实现:

Compiled from “Test2.kt”

//multi 和 doCount 扩展方法在此,且多了一个 Xunit 类型的第一个参数,所以本质是一种封装实现

public final class Test2Kt {

//两扩展方法为静态方法

public static final int multi(Xunit, int, int);

public static final void doCount(Xunit, int);

public static final void testRun();

public static final void main();

public static void main(java.lang.String[]);

}

通过反编译可以发现,扩展本身不会真正的修改目标类,而是被编译进了定义的类中。此外可以发现扩展函数的解析是静态分发的,不是动态的,不支持多态,调用只取决于对象的声明类型,而不是运行实际类型,如下:

open class Parent

class Children: Parent()

fun Parent.method() = “parent”

fun Children.method() = “children”

/**

运行结果:

parent

children

parent

*/

fun testRun() {

val tmp1 = Parent()

println(tmp1.method())

val tmp2 = Children()

println(tmp2.method())

val tmp3: Parent = Children()

println(tmp3.method())

}

接着再来看下扩展方法是否能够被继承的表现,如下两个例子:

open class Parent

class Children: Parent()

fun Parent.method() = “parent”

/**

运行结果:

parent

parent

parent

*/

fun testRun() {

val tmp1 = Parent()

println(tmp1.method())

val tmp2 = Children()

println(tmp2.method())

val tmp3: Parent = Children()

println(tmp3.method())

}

open class Parent

class Children: Parent()

fun Children.method() = “children”

/**

编译报错

*/

fun testRun() {

val tmp1 = Parent()

//println(tmp1.method()) 编译错误:找不到方法 method

val tmp2 = Children()

println(tmp2.method())

val tmp3: Parent = Children()

//println(tmp3.method()) 编译错误:找不到方法 method

}

当扩展函数签名与被扩展类已有函数签名发生重名操作时(尽量不要这么做),则类原有方法优先级更高。案例如下:

fun Sss.method() {

println(“sss method 2”)

}

fun Sss.method(str: String) {

println(“sss method 2 $str”)

}

/**

运行结果:

sss method 1

sss method 2 666

*/

fun testRun() {

val ss = Sss()

//如果类中方法与其扩展方法签名完全一致,则类原有方法优先级更高

ss.method()

//扩展支持方法重载

ss.method(“666”)

}

此外,kotlin 还可以对可空类型进行扩展,使用案例如下:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

fun Any?.toString(): String {

if (this == null) {

return “null—”

}

return toString()

}

/**

运行结果:

null—

1

*/

fun testRun() {

var tmp: Int? = null

println(tmp.toString())

tmp = 1

println(tmp.toString())

}

除过扩展函数,kotlin 的扩展属性与扩展函数基本类似。案例如下:

class Fff

val Fff.name: String get() = “gongjiang”

/**

运行结果:

gongjiang

*/

fun testRun() {

val ff = Fff()

println(ff.name)

}

此外我们还可以对伴生对象进行扩展。如下案例:

class Fff {

companion object {

val name: String = “999”

fun go() {

println(“go–$name”)

}

}

}

fun Fff.Companion.method() {

println(“method–${this.name}”)

}

/**

运行结果:

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

当扩展函数签名与被扩展类已有函数签名发生重名操作时(尽量不要这么做),则类原有方法优先级更高。案例如下:

fun Sss.method() {

println(“sss method 2”)

}

fun Sss.method(str: String) {

println(“sss method 2 $str”)

}

/**

运行结果:

sss method 1

sss method 2 666

*/

fun testRun() {

val ss = Sss()

//如果类中方法与其扩展方法签名完全一致,则类原有方法优先级更高

ss.method()

//扩展支持方法重载

ss.method(“666”)

}

此外,kotlin 还可以对可空类型进行扩展,使用案例如下:

//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober】

fun Any?.toString(): String {

if (this == null) {

return “null—”

}

return toString()

}

/**

运行结果:

null—

1

*/

fun testRun() {

var tmp: Int? = null

println(tmp.toString())

tmp = 1

println(tmp.toString())

}

除过扩展函数,kotlin 的扩展属性与扩展函数基本类似。案例如下:

class Fff

val Fff.name: String get() = “gongjiang”

/**

运行结果:

gongjiang

*/

fun testRun() {

val ff = Fff()

println(ff.name)

}

此外我们还可以对伴生对象进行扩展。如下案例:

class Fff {

companion object {

val name: String = “999”

fun go() {

println(“go–$name”)

}

}

}

fun Fff.Companion.method() {

println(“method–${this.name}”)

}

/**

运行结果:

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

[外链图片转存中…(img-i1UtDinL-1714504787396)]

[外链图片转存中…(img-3NNwmipo-1714504787397)]

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值