在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。如在类中可以再定义一个类,这样的类是嵌套类,其他语法结构也是一样。嵌套类类似于Java中的内部类。
对于Scala来说,成员内部类和静态内部类存在不同的位置中,成员内部类正常的存在于类的成员位置中,而静态内部类因为是静态性质的,所以声明在类对应的伴随对象中。
使用的案例如下:
class ScalaOuterClass { class ScalaInnerClass { //成员内部类 } } object ScalaOuterClass { //伴生对象 class ScalaStaticInnerClass { //静态内部类 } }
1、创建内部类的方法
object ScalaInnerClass { def main(args: Array[String]): Unit = { //创建两个外部类的实例 val outer1 : ScalaOuterClass = new ScalaOuterClass(); val outer2 : ScalaOuterClass = new ScalaOuterClass(); //在scala中创建成员内部类的语法是:new 对象.内部类 //默认情况下,内部类实例和外部对象关联 val inner1 = new outer1.ScalaInnerClass val inner2 = new outer2.ScalaInnerClass //创建静态内部类实例 val staticInnerClass = new ScalaOuterClass.ScalaStaticInnerClass } }
2、访问外部类属性的方法
(1)外部类名.this.属性名
可以通过外部类对象访问外部类的属性
class ScalaOuterClass { var name:String = "scott" private var sal:Double = 1.2 class ScalaInnerClass { //成员内部类 def info()={ // 内部类如果想要访问外部类的属性,可以通过外部类对象访问。 // 即访问方式:外部类名.this.属性名 println("name="+ScalaOuterClass.this.name+"age="+ ScalaOuterClass.this.sal) } } } object ScalaOuterClass { //伴生对象 class ScalaStaticInnerClass { //静态内部类 } }
外部类名.this相当于是这个外部类的一个实例,然后通过“外部类名.this”这个实例对象去访问属性
(2)外部类名别名.属性名
内部类如果想要访问外部类的属性,也可以通过外部类别名访问(推荐)
class ScalaOuterClass { myOuter => //这样写,你可以理解成这样写,myOuter就是代表外部类的一个对象. class ScalaInnerClass { //成员内部类 def info() = { println("name = " + ScalaOuterClass.this.name + " age =" + ScalaOuterClass.this.sal) println("-----------------------------------") println("name = " + myOuter.name + " age =" + myOuter.sal) }} // 当给外部指定别名时,需要将外部类的属性放到别名后. var name : String = "scott" private var sal : Double = 1.2 }
外部类名.this 等价 外部类名别名,需要注意的是当给外部指定别名时,需要将外部类的属性放到别名后。
二、类型投影
首先,展示一段代码中的一个错误:
class ScalaOuterClass { myOuter => class ScalaInnerClass { //成员内部类 def test(ic:ScalaInnerClass): Unit = { System.out.println("使用了类型投影:"+ic) } } object Scala01_class { def main(args: Array[String]): Unit = { val outer1 : ScalaOuterClass = new ScalaOuterClass(); val outer2 : ScalaOuterClass = new ScalaOuterClass(); val inner1 = new outer1.ScalaInnerClass() val inner2 = new outer2.ScalaInnerClass() inner1.test(inner1) inner1.test(inner2) // inner1.test(inner2) // error, 需要outer1.ScalanInner而inner2属于outer2.ScalanInner } }
在此段代码中,inner1.test(inner2)会发生编译错误,因为在Scala中,内部类实例和外部类的对象是关联的,并不是像Java中那样只看类型,此时inner1中的test方法的参数类型要求的是outer1.ScalaInnerClass。
Java中的内部类从属于外部类,因此在java中inner.test(inner2) 就可以,因为是按类型来匹配的。
Scala中内部类从属于外部类的对象,所以外部类的对象不一样,创建出来的内部类也不一样,无法互换使用
所以为了解决这种问题,那么就引出了类型投影的技术,即在方法声明上,如果使用 外部类#内部类 的方式,表示忽略内部类的对象关系,等同于Java中内部类的语法操作,我们将这种方式称之为类型投影(即:忽略对象的创建方式,只考虑类型)
将上面的代码中的test方法的参数类型修改为"ic: ScalaOuterClass#ScalaInnerClass",即可解决这样的错误。所以类型投影的作用就是屏蔽外部对象对内部类对象的影响。