把一个类放在另一个类的内部定义,定义在其他类内部的类就被称为嵌套类,包含嵌套类的类被称为外部类把一个类放在另一个类的内部定义,定义在其他类内部的类就被称为嵌套类,包含嵌套类的类被为部。
- 嵌套类:只要将一个类放在另一个类中定义,这个类就变成了嵌套类,相当于Java中static修饰的静态内部类。
- 内部类:使用inner修饰的嵌套类叫内部类,相当于Java中无static修饰的非静态内部类。
嵌套类的主要作用如下:
- 嵌套类提供了更好的封装,可以把嵌套类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。
嵌套类定义的语法格式如下:
class OuterClass{
//此处可以定义嵌套类、内部类
}
大部分时候,嵌套类(内部类)都被作为成员嵌套类(内部类)定义,而不是作为局部嵌套类。成员嵌套类是一种与属性、方法、构造器和初始化块相似的成员;局部嵌套类则不是类成员。
嵌套类无须使用任何特殊修饰符,它相当于Java的静态内部类;内部类需要使用inner修饰,它相当于java的非静态内部类。
一、内部类
内部类相当于外部类的实例成员,所以它可以直接访问外部类的所有成员。
//通过主构造器为外部类定义属性
class Cow(var weight: Double = 0.0) {
//定义一个内部类
//通过主构造器为内部类定义属性
private inner class CowLeg(var length: Double = 0.0, var color: String = "") {
//内部类的方法
fun info() {
println("这只牛腿的颜色是:${color},高:${length}")
//直接访问外部类的private修饰的foo()方法
foo()
}
}
fun test() {
val cl = CowLeg(1.12, "红色")
cl.info()
}
private fun foo() {
println("Cow的foo方法")
}
}
fun main(args: Array<String>) {
val cow = Cow(378.9)
cow.test()
}
输出结果:
这只牛腿的颜色是:红色,高:1.12
Cow的foo方法
如果外部类属性、内部类属性与内部类中方法的局部变量同名,则可通过使用this、带标签的this进行限定来区分。
package `0704`
class DiscernVariable {
//隐式标签@DiscernVariable
private val prop = "外部类的属性"
inner class InClass {
//隐式标签@InClass
private val prop = "内部类的属性"
fun info() {
val prop = "局部属性"
//通过外部类类名.this.varName访问外部类的属性
println("外部类的属性值:${this@DiscernVariable.prop}")
//通过this.varName访问内部类的属性
println("内部类的属性值:${this.prop}")
//直接访问局部变量
println("局部变量的值:${prop}")
}
}
fun test() {
val ic = InClass()
ic.info()
}
}
fun main(args: Array<String>) {
DiscernVariable().test()
}
输出结果:
外部类的属性值:外部类的属性
内部类的属性值:内部类的属性
局部变量的值:局部属性
Kotlin关于this的处理规则如下:
- 在类的方法或属性中,this代表调用该方法或属性的对象;
- 在类的构造器中,this代表该构造器即将返回的对象;
- 在扩展函数或带接收者的函数字面值中,this表示点左边的“接收者”;
- 如果this没有限定符,那么它优先代表包含该this的最内层的接收者,并且会自动向外搜索。如果要让this明确引用特定的接收者,则可使用标签限定符。
package `0704`
class A {
//隐式标签@A
inner class B {
//隐式标签@B
//为Int扩展foo()方法
fun Int.foo() {//隐式标签@foo
val a = this@A//A的this
val b = this@B//B的this
val c = this//不带标签的this,默认代表该方法所属对象:Int对象
val c1 = this@foo//显示指定@foo标签,与c代表的对象相同
println(a)
println(b)
println(c)
println(c1)
//为String扩展funLit()方法
val funLit = lambda@ fun String.() {
val d = this//不带标签的this,默认代表该方法所属对象:String对象
val d1 = this@lambda//显式指定@lambda标签,与d代表的对象相同
println(d)
println(d1)
}
"kotlin".funLit()
val funLit2 = {
val e = this//this代表foo()方法的接收者:Int对象
val e1 = this@foo//显式指定@foo标签,与e代表的对象相同
println("foo()方法中Lambda表达式的this:" + e)
println("e1的this:" + e1)
}
funLit2()
}
fun testB() {
2.foo()
}
}
fun testA() {
var bObj = B()
println("程序创建的B对象:${bObj}")
bObj.testB()
}
}
fun main(args: Array<String>) {
var aObj = A()
println("程序创建的A对象:${aObj}")
aObj.testA()
}
输出结果:
程序创建的A对象:0704.A@256216b3
程序创建的B对象:0704.A$B@d7b1517
0704.A@256216b3
0704.A$B@d7b1517
2
2
kotlin
kotlin
foo()方法中Lambda表达式的this:2
e1的this:2
内部类的成员只在内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问内部类的成员,则必须显式创建内部类对象来调用访问其成员。
package `0704`
class Outer {
private val outProp = 9
inner class Inner {
val inProp = 5
fun acessOuterProp() {
//内部类可以直接访问外部类的private属性
println("外部类的outProp值:${outProp}")
}
}
fun accessInnerProp() {
//如需访问内部类的属性,必须显式创建内部类对象
println("内部类的inProp值:${Inner().inProp}")
}
}
fun main(args: Array<String>) {
val ot = Outer()
ot.accessInnerProp()
}
输出结果:
内部类的inProp值:5
二、嵌套类
嵌套类直接属于外部类的类本身,而不是外部类实例相关。
Java语法有一条规则:静态成员不可访问非静态成员。
Kotlin类中的成员除嵌套类之外,全部都是非静态成员,嵌套类不可访问外部类的其他任何成员,只能访问另一个嵌套类。
package `0705`
class NestedClassTest {
var prop1 = 5
fun test() {
println("外部类的test()方法")
}
//没有inner修饰符,是嵌套类
class NestedClass {
fun accessOuterMember() {
//访问另一个嵌套类
val a = A()
//下面注释掉的代码是错误的
// test()
// println(prop1)
}
}
class A
}
嵌套类唯一可访问的是外部类的其他嵌套类。
嵌套类相当于外部类的静态成员,因此外部类的所有方法、属性、初始化块都可以使用嵌套类来定义变量、创建对象等。
外部类不能直接访问嵌套类的成员,但可以使用嵌套类的对象作为调用者来访问嵌套类的成员。
class AccessNestedClass {
class NestedClass {
var prop = 9
}
fun accessNestedProp() {
//通过对象访问嵌套类的成员
println(NestedClass().prop)
}
}
Kotlin还允许在接口中定义嵌套类,但不允许在接口中定义内部类。
三、在外部类以外使用内部类
定义类的主要作用就是定义变量、创建对象和派生子类。
因为内部类的对象必须寄生在外部类的对象中,因此在创建内部类对象之前,必须先创建其外部类对象。
package `0705`
class Out {
//定义一个内部类,不适用访问控制符,默认是public
inner class In(msg: String) {
init {
println(msg)
}
}
}
fun main(args: Array<String>) {
var oi :Out.In=Out().In("测试信息")
}
四、在外部类以外使用嵌套类
因为嵌套类是属于外部类的类本身的,因此创建嵌套类对象时无须创建外部类对象。
package `0705`
class NestedOut {
//定义一个嵌套类,不适用访问控制符,默认是public
open class Nested {
init {
println("嵌套类的构造器")
}
}
}
fun main(args: Array<String>) {
val nested: NestedOut.Nested = NestedOut.Nested()
}
不管是内部类还是嵌套类,其声明变量的语法完全一样。区别只是在创建对象时,嵌套类只需使用外部类即可调用构造器,而内部类必须使用外部类对象来调用构造器。
五、局部嵌套类
如果把一个嵌套类放在方法或函数中定义,就是一个局部嵌套类,局部嵌套类仅在该方法或函数中有效。
如果需要用局部嵌套类定义变量、创建实例或派生子类,都只能在局部嵌套类所在的方法(或函数)内进行。
package `0705`
class LocalNestedClass {
fun info() {
//定义局部嵌套类
open class NestedBase(var a: Int = 0) {}
//定义局部嵌套类的子类
class NestedSub(var b: Int = 0) : NestedBase()
//创建局部嵌套类的对象
val ns = NestedSub()
ns.a = 5
ns.b = 8
println("NestedSub对象的a和b属性是:${ns.a},${ns.b}")
}
}
fun main(args: Array<String>) {
LocalNestedClass().info()
}
输出结果:
NestedSub对象的a和b属性是:5,8
六、匿名内部类
Kotlin抛弃了匿名内部类的功能,而提供了一个更强大的语法:对象表达式。
如果对象是函数式接口的实例,则可使用带接口类型前缀的Lambda表达式创建它。
fun main(args: Array<String>) {
//使用Lambda表达式创建Runnable实例
var t = Runnable {
for (i in 0..100) {
println("${Thread.currentThread().name},i:${i}")
}
}
//启动新线程
Thread(t).start()
//主线程的循环
for (i in 0..100) {
println("${Thread.currentThread().name},i:${i}")
}
}
网友评论