秒懂Kotlin之彻底弄懂属性(Property)Part1

[版权申明] 非商业目的注明出处可自由转载
博文地址: https://blog.csdn.net/ShuSheng0007/article/details/109049970
出自:shusheng007

系列文章:
二奶Kotlin上位记
秒懂Kotlin之彻底弄懂属性(Property)Part2

AD: 认准公众号《华北01学会》,带你装逼带你飞!——ShuSheng007

俺默认你会Java

概述

接触过Kotlin的都知道,其增加了属性的语法(Properties),多年前我就在C#中见过了。可以说Kotlin把属性玩出了花,初识kotlin时不容易完全掌握,而属性又是在日常编程时大量使用的,所以我们应该彻底掌握这一利器,看完本文,相信你会对属性做到胸有成竹。

如何声明

要掌握一个知识,首先要能熟练的使用它。 属性其实是kotlin的又一颗语法糖,对应Java的settergetter方法,但又有些许不同。

最复杂的声明

完整的属性声明语法如下

var <属性名称>[: <属性类型>] [= <属性初始化器>]
    [<getter>]
    [<setter>]

其中,初始化器、gettersetter都是可以省略的。我们来为Student类声明一个age属性,如下所示

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引用即可。

最简单的声明

上面那个是最复杂的属性声明,我们大部分时间用不上,平时都是用最简单的版本。

var gender: String = "人妖"

是不是感觉就像在Java中声明了一个public字段啊,其实编译器还是做了很多工作的,它为我们生成了默认的settergetter方法,如下所示:

 //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的是属性的gettersetter方法,而不是生成的那个字段。

让我们看一下代码:

父类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了。其背后的原理我们已经讨论过了,就是因为编译器会为DogAninmal产生签名一样的方法,最终转换为方法的重写了。

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改进你觉得有必要吗?

最后点赞加分享是猿猿们独有的美德!

只有把抱怨环境的心情,化为上进的力量,才是成功的保证。——罗曼·罗兰

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShuSheng007

亲爱的猿猿,难道你又要白嫖?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值