Kotlin中的嵌套类和内部类

作者: 程序员丶星霖 | 来源:发表于2019-11-21 10:43 被阅读0次

    把一个类放在另一个类的内部定义,定义在其他类内部的类就被称为嵌套类,包含嵌套类的类被称为外部类把一个类放在另一个类的内部定义,定义在其他类内部的类就被称为嵌套类,包含嵌套类的类被为部。

    • 嵌套类:只要将一个类放在另一个类中定义,这个类就变成了嵌套类,相当于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}")
        }
    }
    

    学海无涯苦作舟

    我的微信公众号.jpg

    相关文章

      网友评论

        本文标题:Kotlin中的嵌套类和内部类

        本文链接:https://www.haomeiwen.com/subject/wkerhctx.html