如何理解kotlin的中属性

属性的定义

kotlin中,varval是用来声明属性的两个关键字,在kotlin官方参考文档上是这么说的。

Kotlin的类可以有属性。 属性可以用关键字 var声明为可变的,否则使用只读关键字val

那什么是可变属性,什么又是可读属性呢?我们在问这个问题前可以先回顾一下java对属性的定义。

属性可以通过getsetis(可以替代get,用在布尔型属性上)方法或遵循特定命名规范的其他方法访问。

在这里我们可以理解为,在java中,对一个字段生成publicset get方法,那么他就是一个可变(可读可写)属性,仅提供public get或者setprivate修饰的就是一个只读属性。

在kotlin中我们是如何定义属性的呢

class Person {
    //定义一个String类型的属性name
    var name: String? = null
    //定义一个Boolean类型的属性deceased
    var deceased: Boolean? = false
}

kotlinjava同属于jvm语言,编译后会生成class文件。我将上面的kotlin代码生成的class再次反编译成java代码,如下:

public final class Person {
   @Nullable
   private String name;
   @Nullable
   private Boolean deceased = false;

   @Nullable
   public final String getName() {
      return this.name;
   }

   public final void setName(@Nullable String var1) {
      this.name = var1;
   }

   @Nullable
   public final Boolean getDeceased() {
      return this.deceased;
   }

   public final void setDeceased(@Nullable Boolean var1) {
      this.deceased = var1;
   }
}

看到这里你会发现,反编译后代码和java属性的定义是一毛一样有木有。所以kotlin的属性,在java代码中可以通过set get方法来调用。

只读属性和常量

有的地方说,var是声明变量的,val是声明常量的。这个其实不是完全正确,至少val在官方参考文档上说的是只读属性,而不是常量。比方说温度,我们可以获取温度的变化,但是不能人为直接改变温度,那么温度就是只读属性,而不是常量。
再比如说list的isEmpty属性

val isEmpty get() = this.size == 0

isEmpty属性受到size的改变而改变,但是无法主动去设置isEmpty的值。那么isEmpty就是个只读属性。

在这里要分为几种情况来理解val

  • val声明的属性被字面量或者表达式赋值的时候
  • val声明的属性实现了自定义的get方法

看下面的例子

class Person {
    //这里的name属性被字面量“zhangsan”赋值了
    val name: String? = "zhangsan"
     //age属性的值受birthday的影响
    val age: Int get() {
        return Calendar.getInstance().get(Calendar.YEAR) - birthday
    }
    var birthday: Int = 0
}

反编译成java代码是这样的

public final class Person {
    //name属性被字面量赋值,编译后会加上final关键字,成为真正的常量
   @Nullable
   private final String name = "zhangsan";
   private int birthday;

   @Nullable
   public final String getName() {
      return this.name;
   }
    
    //age属性只实现了get方法,无法通过set方法来改变值,是个只读属性
   public final int getAge() {
      return Calendar.getInstance().get(1) - this.birthday;
   }

   public final int getBirthday() {
      return this.birthday;
   }

   public final void setBirthday(int var1) {
      this.birthday = var1;
   }
}

什么是变量的幕后字段

kotlin中可以通过关键字声明属性,那可不可以声明字段呢?它有没有字段呢?
答:kotlin中不能声明字段,但是kotlin中是有字段的,他有一个幕后字段的概念,每一个属性可以有一个幕后字段,也可以没有。

拿上面的例子再讲一遍

val isEmpty get() = this.size == 0

isEmpty的值只和size有关,那么在反编译之后,不会出现如下代码

private boolean isEmpty = false;

这里可以得出一个结论,属性不一定非得有字段。对java属性定义的后半句是这么说的,“或遵循特定命名规范的其他方法访问”,所以关键看方法的定义,比如说在一个类里面有setWidth(),getWidth()这两个方法。就可以认定为这个类有width属性,但是不一定有width这个字段。

也就是说这个属性没有幕后字段。

再看下面这个例子

//name属性没有实现自定义的set get方法
var name:String? = null;
......

name = "zhangsan"

val len = name.length()

name没有实现自定义的set get方法,字面值"zhangsan"需要一个字段来赋值。那么在class里面会生成

//这个就是name属性的幕后字段
private String name = null;

那么幕后字段可以用代码访问到么,这个是可以的,使用 field 关键字

var name: String? = null

//上面这句代码其实和下面的代码是等价的,没有区别。
var name: String? = null
   set(value) {
       field = value
   }
   get() {
       return field
   }

讲到这里我相信大家对幕后字段已经有了一定的了解。

扩展属性

在kotlin声明一个扩展属性的方法如下。

val Person.city:String get() { return "city" } 

注:扩展属性只能用生成自定义set get方法来实现,不能直接用字面量来赋值。所以下面的代码是错的

val Person.city:String = "city"

第二句代码是无法编译通过的,那是因为

扩展是静态解析的.扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在一个类中插入新成 员, 仅仅是可以通过该类型的变量用点表达式去调用这个新函数,或者属性。由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就 是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 get/set 定义。

静态解析的结果是

  @NotNull
   public static final String getCity(@NotNull Person $receiver) {
      return "city";
   }

所以扩展仅仅是可以通过点语法来调用,并不是真正插入类里面,也不存在幕后字段。

总结

  • 属性可以通过getsetis(可以替代get,用在布尔型属性上)方法或遵循特定命名规范的其他方法访问。
  • 只读属性和常量不是同一个概念,具体看上头。
  • 属性可以有一个幕后字段,也可以没有
  • 扩展是静态解析的,扩展属性是没有幕后字段的
  • 扩展只是能通过点语法调用而已。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350