目录
属性的声明
在主构造函数中声明
在主构造函数中采用 var/val 变量:类型 即可声明属性
open class A(var name:String,var node:String){ override fun toString(): String { return "A(name='$name', node='$node')" //name,node即属性 } } fun main(args:Array<String>){ var a=A("Hellen","1") println(a) //A(name='Hellen', node='1') }
在属性初始化器中声明
具体格式为:
var/val 属性名 [ : 属性类型 ] [ =值 ]
[ 访问权限符 set()=/set(){} ]
[ 访问权限符 get()=/get(){} ]
其中 [ ] 内为可选部分,var变量有get和set方法,而val变量只有get方法
当属性类型可以由set和get推断出时,属性类型可省略,但val只有get方法,必须声明类型
一般指定值,就可省略属性类型
var count=1 open class A{ var name:String //有了默认get,自定义了set和构造函数,值省略 private set(value){ field=value println("set name's value") } val node:Int get() { return count++ }//只有get默认,值不能省略。可指定值,省去类型 constructor(name:String){ this.name=name } override fun toString(): String { return "A(name='$name', node=$node)" } } fun main(args:Array<String>){ var a=A("Hellen") //set name's value var b=A("Peter") //set name's value var c=A("John") //set name's value println(a) //A(name='Hellen', node=1) println(b) //A(name='Peter', node=2) println(c) //A(name='John', node=3) }
属性的使用
一般属性在类中可直接用属性名访问,有名称冲突时可以使用this.属性名;在类外可以通过 对象.属性名 访问public属性
var count=1 open class A{ var name:String set(value) { field=value println("name's set") } val node get() = count++ var kotlin_score:Int val kotlin_level:String get() { println("kotlin_level's get") return when(kotlin_score/10){ 9,10->{ "A" } 8->{ "B" } 6,7->{ "C" } else->{ "D" } } } constructor(name:String,kotlin_score:Int){ this.name=name //名称冲突,用this.属性名访问 this.kotlin_score=kotlin_score //名称冲突,用this.属性名访问 //this.属性名=变量/值,默认调用属性名的set函数 } override fun toString(): String { //无名称冲突,直接用属性名访问 return "A(name='$name', node=$node, kotlin_score=$kotlin_score, kotlin_level='$kotlin_level')" //$属性名,会默认调用属性名的get函数 print(属性名)等需要属性名值的地方都是默认调用了属性名的get函数 } } fun main(args:Array<String>){ var a=A("Hellen",85) //name's set var b=A("Peter",100) //name's set var c=A("John",67) //name's set println(a) //kotlin_level's get //A(name='Hellen', node=1, kotlin_score=85, kotlin_level='B') println(b) //kotlin_level's get //A(name='Peter', node=2, kotlin_score=100, kotlin_level='A') println(c) //kotlin_level's get //A(name='John', node=3, kotlin_score=67, kotlin_level='C') println(c.name+" "+c.kotlin_score)// 类外可调用对象的public属性,对象.属性名 }
幕后字段
幕后字段主要是解决属性的set函数调用赋值时的内存溢出问题,主要用于让一个属性在不同的条件下有不同的值
一般按照Java的理解,set函数会写成如下这样的形式
var name:String set(value) { this.name=value }
这样写IDEA会报黄,显示未分配setter
这样运行以后也会出现报错:栈溢出StackOverflowError
将Kotlin类转换为Java如下,可见有setName循环调用
public class A { @NotNull private String name; @NotNull public final String getName() { return this.name; } public final void setName(@NotNull String value) { Intrinsics.checkNotNullParameter(value, "value"); this.setName(value);//循环调用 } @NotNull public String toString() { return "A(name='" + this.name + "')"; } public A(@NotNull String name) { Intrinsics.checkNotNullParameter(name, "name"); super(); this.setName(name); } }
因此为了解决这个问题,就引出了幕后字段:在Kotlin中, 如果属性至少一个访问器使用默认实现,那么Kotlin会自动提供幕后字段。幕后字段用关键字field表示,指代当前的属性,只能用于get和set函数中,也因此,set函数写作如下:
var name:String set(value) { field=value //field指代属性name }
注意:并不是所有的Kotlin属性都有幕后字段:要满足以下两种情况之一:set和get其中之一采用默认实现;自定义的set和get中使用field
幕后属性
幕后属性满足:对类外只可读,但在类内既可读又可写
实现幕后属性需要两个变量的声明:
(官方这样要求,但应该可以通过声明var属性但是自定义 public get,private set 实现幕后属性的特点,只是反编译的Java代码不同)
private var a: Int=0 public val m_a: Int get() = a //a即为幕后属性 //反编译为: //public class A { // private int a; // // public final int getM_a() { // return this.a; // } //}
var a:Int=0 private set(value){ field=value }//get默认public //反编译为 //public class A { // private int a; // // public final int getA() { // return this.a; // } // // private final void setA(int value) { // this.a = value; // } //} // 较官方使用,多反编译了set函数