欢迎大家来到IT世界,在知识的湖畔探索吧!
俺默认你会Java
概述
接触过Kotlin的都知道,其增加了属性的语法(Properties),多年前我就在C#中见过了。可以说Kotlin把属性玩出了花,初识kotlin时不容易完全掌握,而属性又是在日常编程时大量使用的,所以我们应该彻底掌握这一利器,看完本文,相信你会对属性做到胸有成竹。
如何声明
要掌握一个知识,首先要能熟练的使用它。 属性其实是kotlin的又一颗语法糖,对应Java的setter与getter方法,但又有些许不同。
最复杂的声明
完整的属性声明语法如下
var <属性名称>[: <属性类型>] [= <属性初始化器>] [<getter>] [<setter>]
欢迎大家来到IT世界,在知识的湖畔探索吧!
其中,初始化器、getter、setter都是可以省略的。我们来为Student类声明一个age属性,如下所示
欢迎大家来到IT世界,在知识的湖畔探索吧!class Student { var age: Int = 18 set(value) { if (value > 100) { println("老而不死是为妖") } else { field = value } } get() { return field + 1 } ... }
上面代码声明了一个可变属性age, 初始化为18,当被赋值大于100时不合法,当获取年龄时返回当前值加1。
有几点注意的地方,set中的value可以随便命名,但推荐使用value。field是kotlin的一个关键字,代表编译器帮助生成的那个private字段。
我们来看看其对应的Java代码:
public final class Student{ private int age=18; public final int getAge() { return this.age + 1; } public final void setAge(int value) { if (value > 100) { String var2 = "老而不死是为妖"; System.out.println(var2); } else { this.age = value; } } }
是不是有似曾相识的感觉,就是个语法糖,实际上是使用字段加方法实现的。其中private int age 对应的就是kotlin中的field,back field 不一定每次都产生,因为其对我们是透明的,所以我们不用关心它,用的时候就在setter或者getter方法中使用field引用即可。
最简单的声明
上面那个是最复杂的属性声明,我们大部分时间用不上,平时都是用最简单的版本。
欢迎大家来到IT世界,在知识的湖畔探索吧!var gender: String = "人妖"
是不是感觉就像在Java中声明了一个public字段啊,其实编译器还是做了很多工作的,它为我们生成了默认的setter与getter方法,如下所示:
//Java @NotNull private String gender = "人妖"; @NotNull public final String getGender() { return this.gender; } public final void setGender(@NotNull String var1) { ... this.gender = var1; }
省略初始化器的情形:
上面的例子中,在声明属性时都必须提供初始化器,就是那个为属性赋值的操作,那么什么情况下可以不用为属性赋初始值呢?如下几个情形初始化器就可以省略。
- 使用lateinit关键字声明的属性lateinit var studyResult: String 但是要求在使用此属性前必须要完成初始化,不然会抛运行时异常。
- 在类的主构造函数中声明的属性class Student (val clothes: String) 其初始化会在构造函数中完成,是对应的Java代码如下@NotNull private final String clothes; public Student(@NotNull String clothes) { … this.clothes = clothes; } @NotNull public final String getClothes() { return this.clothes; }
最开始接触Kotlin时我对这个语法时还是比较懵逼的,会将其与参数不带val 版本混淆。
class Student (clothes: String)
上面代码的clothes仅仅是Student构造函数传入的一个参数,只能在init块中使用,相当于Java的构造函数{}。
class Student (clothes: String){ lateinit var studyResult: String init { studyResult = clothes } }
- 只有getter方法的属性
val height: Int get() { return 180 }
Private属性
kotlin属性默认都是public的, 如果你把属性声明为Private的话,那它就真的成为一个私有字段了,编译器不会产生任何额外代码
//kotlin private val _name: String = "ben" private var _name: String = "ben" //java private final String _name="ben"; private String _name="ben";
顶级属性
我们知道,在Java中一切都要基于类。像字段,方法都必须包含在类里面,但是Kotlin却允许将属性、方法、类直接写在文件当中。那么类里面的属性与文件中的属性有什么异同呢?
由于Kotlin/JVM最终是要编译成Java字节码的,所以它的这些属性其实也可以看做是语法糖。kotlin编译器会以 文件名称加上Kt后缀为类名生成一个新类,而其中的属性相应的会成为此类的成员,与直接写在类里面的属性不同的是他们都是static的。
例如一个叫KotlinFile文件,内容如下:一个属性,一个常量
package top.ss007.learn.kotlin.classes var topProperty: String = "" set(value) { field = value } get() { return "hello property" } const val CONST="I AM A CONSTANT"
其对应的Java代码为
public final class KotlinFileKt { @NotNull private static String topProperty = ""; @NotNull public static final String CONST = "I AM A CONSTANT"; @NotNull public static final String getTopProperty() { return "hello property"; } public static final void setTopProperty(@NotNull String value) { Intrinsics.checkNotNullParameter(value, "value"); topProperty = value; } }
可以看到,kotlin编译器帮助生成了一个新类KotlinFileKt,属性相关的生成字段与方法都属于此类,而且是static的。
Override 属性
Overriding Properties 这个就比较颠覆Java程序员的认知了,在Java中字段是不支持override的。
例如我们有如代码
//java public class JAnimal { public int weight = 100; } public class JDog extends JAnimal { public int weight = 200; } //调用 JAnimal animal = new JDog(); System.out.println(String.format("The weight of Java animal is %d", animal.weight));
上面的代码执行后会输出什么呢?是100还是200呢?
输出结果:
The weight of Java animal is 100
输出结果是Animal类中字段的值100,可见Java不支持字段的override。所以上面是非常糟糕的代码,我们一定要避免在子类中定义与父类相同名称的字段。
同为JVM语言,那为什么Kotlin就可以呢?这其实又是一颗语法糖,实际上override的是属性的getter和setter方法,而不是生成的那个字段。
让我们看一下代码:
父类Animal和子类Dog中分别有两个签名一模一样的属性,只读属性weight与可变属性name,当name被设置时会打印一句log。
open class Animal { open val weight: Int = 100 open var name: String = "animal" set(value) { field = value println("Animal被设置为$value") } } class Dog : Animal() { override val weight: Int = 200 override var name: String = "dog" set(value) { field = value println("Dog被设置为$value") } } class Cat(override var weight: Int) : Animal() { }
注意父类Animal属性前面的open以及子类Dog属性前面的override
执行验证结果:
fun runPropertyDemo() { val animal: Animal = Dog() //如果weight被override了就会输出Dog里的值200,否则输出Animal里的值100 println("The weight of specific animal is ${animal.weight}") println("The name of dog is ${animal.name}") //如果name被override了就会调用Dog里面的setter方法,否则调用Animal里的 animal.name = "藏獒" println("The name of dog is ${animal.name}") }
输出结果:
The weight of specific animal is 200 The name of dog is dog Dog被设置为藏獒 The name of dog is 藏獒
从结果可以看出,在kotlin中属性确实被override了。其背后的原理我们已经讨论过了,就是因为编译器会为Dog与Aninmal产生签名一样的方法,最终转换为方法的重写了。
public class Animal { private final int weight = 100; public int getWeight() { return this.weight; } ... } public final class Dog extends Animal { private final int weight = 200; public int getWeight() { return this.weight; } }
看看上面转换后的Java代码,就差给getWeight()方法加上一个@override注解了。
代理属性
这块稍微有点复杂,内容较多,本片文章已经比较长了,留作下篇单独介绍。
总结
总的来说,Kotlin的属性相对于Java来说就是一颗接一颗的语法糖,你们喜欢Kotlin属性这颗语法糖吗?这块对Java改进你觉得有必要吗?我们留言讨论!
最后点赞加分享是猿猿们独有的美德!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/65490.html