Scala入门-包


Scala 的包机制,在 Java 基础之上更加的灵活。

包路径

Scala 中包名和源码所在的系统文件目录结构要可以不一致(Java 中强制要求一致),但是编译后的字节码文件路径和包路径会保持一致(编译器自动完成)。

系统目录结构如下:

此时如果我们修改 Bird1 的打包路径:

再看看源代码和字节码文件所在的路径,完全不同:

为了让大家看的更清晰,再付上一张图:

很明显,目录结构和包的引用路径不一致,目录结构中没有 package100 这个包。但是程序并没有问题。因为编译器会在文件系统上自动创建对应的包路径,这里就是会自动创建 package100 这个目录,以保证字节码文件路径和包名保持一致。

包嵌套

包可以像嵌套类那样嵌套使用,即包中有包。这样的好处是,可以在同一个文件中,将 class、 object、trait 创建在不同的包中,非常灵活。

编译运行后查看字节码文件:

也进一步印证了 Scala 中源文件和字节码文件路径可以不一致。

包作用域

一个大括号就是一个作用域。

作用域原则:可以直接向上访问。

在 Scala 中子包可以直接访问父包中的内容(Java 中子包使用父包的类必须使用 import)。在子包和父包类重名时,默认采用就近原则,如果希望指定使用某个类,则带上包名即可。

package com.cris {

  class Apple {

  }

  package scala {

    class Apple {

    }

    object Boy {
      def main(args: Array[String]): Unit = {
        /*
        1. Scala 中子包可以直接访问父包的内容;
        2. 子包和父包的类重名,默认采取就近原则;
        3. 可以带上类的路径名指定使用该类
        */
        val apple = new Apple
        val apple2 = new com.cris.Apple
        // class com.cris.scala.Apple,子包中的 Apple
        println(apple.getClass)
        // class com.cris.Apple,父包中的 Apple
        println(apple2.getClass)
      }
    }
  }
}

但是,父包要访问子包的内容时,需要 import 对应的类。

package com.cris {

  import com.cris.scala.Apple

  object Apple{
    def main(args: Array[String]): Unit = {
      // 推荐只在使用的时候再引用,控制作用域
      import com.cris.scala.Apple
      val apple = new Apple()
      // class com.cris.scala.Apple
      println(apple.getClass)
    }
  }

  package scala {

    class Apple {

    }
  }
}

Scala 也支持在同一个文件中,声明多个并列的 package。

包对象

包可以包含class、object、trait,但不能包含函数或变量的定义。这是 Java 虚拟机的局限。为了弥补这一点不足,scala 提供了包对象的概念来解决这个问题。

package cc.tianny {

   // package object emp 表示创建一个包对象 emp, 它是 cc.tianny.emp 这个包对应的包对象
   // package object emp 会在 cc.tianny.emp 包下生成 package.class 和 package$.class

  package object emp {
    def eat(): Unit = {
      println("eat")
    }

    val salary = 1000.0
  }

  package emp {

    object test {
      def main(args: Array[String]): Unit = {
        eat() // eat=》实质调用了 package$.class 中的 MODULE$.eat()
        println(salary) // 实质调用了 package$.class 中的 MODULE$.salary()
      }
    }

  }
}

包对象字节码路径:

包对象字节码内容:

小结:

  1. 包对象的名字需要和包名一致。
  2. 每一个包都可以有一个包对象。
  3. 包对象的名字需要和子包一样。
  4. 在包对象中定义的变量和方法,便可以在对应的包中使用。

包的可见性

在 Scala 中,包的访问权限与 Java 中有较大的不同。

  1. 当属性访问权限为默认时,从字节码上看属性被声明为 private,但因为提供了 xxx_$eq() 方法(类似setter)和 xxx() 方法(类似 getter),因此从使用效果看是任何地方都可以访问。
  2. 当方法访问权限为默认时,默认为 public 访问权限。
  3. private 为私有权限,只在类的内部和伴生对象中可用。
  4. protected 为受保护权限,只能子类访问,同包无法访问。
  5. 没有 public 关键字,即不能用 public 显式的修饰属性和方法。
  6. 提升属性的访问权限

针对第三小点,看如下代码:

针对第六小点,看如下代码:

默认情况下,Person 类下的 name 属性是私有的,包中的其他类或对象无法访该属性。这时我们对该属性进行了提升,让 detail 包下的类或对象都可以访问该属性。当然,也可以继续提升,让 detail 上层的包也可以访问该属性。

包的引用

Scala 中,import 语句可以出现在任何地方,并不仅限于文件顶部,import 语句的作用一直延伸到包含该语句的块末尾。这种语法的好处是:在需要时在引入包,缩小 import 包的作用范围,提高效率。

示例如下:

如果不想要某个包中全部的类,而是其中的几个类,可以采用选取器大括号的方式。

示例如下:

如果引入的多个包中含有相同的类,那么可以将不需要的类进行重命名进行区分。

或者使用 import java.util.{HashMap => _ } 对冲突的包进行隐藏。


文章作者: Tianny
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Tianny !
评论
 上一篇
Scala入门-封装 Scala入门-封装
提供 get 方法 def setXxx(参数名 : 类型) : Unit = { //加入数据验证的业务逻辑 属性 = 参数名 } 提供 set 方法 def getXxx() [: 返回类型]
2020-03-20
下一篇 
Scala入门-构造器参数 Scala入门-构造器参数
Scala 构造器参数支持三种方式,默认无修饰符、使用 val 关键字声明、使用 var 关键字声明。 无修饰符Scala 类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。 比较简单,就不给出具体代码了。 使用 val 声明如
2020-03-19
  目录