11-包和引入

前言

这里介绍在Scala中,包和引入的相关操作。
会与Java作对比。

1. 将代码放入包中

如果你看过前面的章节会知道,所有的示例代码中都是从object 对象开始。
这里,使用包的形式。
如:

package gyt.navigation
class Navigator

如Java一样,如果你的包名是网站,建议倒着写。
另外一种类型C#的命名空间的做法,称为打包。如:

package gyt.navigation {
  class Navigator
}

打包是一个更加通用的表示法,让我们可以在一个文件里包含多个内容。如,将源代码和测试代码放在一起:

package gyt {
  package navigation {
    // 位于 gyt.navigation 中
    class Navigator
    package tests {
      // 位于 gyt.navigation.tests 中
      class Tests
    }
  }
}

2. 对相关代码的精简访问

将代码按照包层次划分以后,不仅有助于浏览代码,同时告诉编译器,同一个包中的代码之间存在某种相关性。在同一个包中访问代码时,允许使用简短的,不带限定前缀的名称。
如:

package gyt {
  package navigation {
    class Navigator {
      // 简短
      val map = new StarMap
    }
    class StarMap
  }
  class Ship {
    // 加上包名
    val nav = new navigation.Navigator
  }
  package fleets {
    class Fleet {
      // 属于同一级包
      def addShip() = {
        new Ship
      }
    }
  }
}

如果不在同一级包,不能使用简短访问,如:

package gyt {
  class Ship
}
package gyt2.fleets {
  class Fleet {
    def addShip() = {
      // 无法通过编译
      new Ship
    }
  }
}

不再同一级包,需要使用包名,如:

package gyt {
  class Ship
}
package gyt2.fleets {
  class Fleet {
    def addShip() = {
      // 能够通过编译
      new gyt.Ship
    }
  }
}

位于不同文件,使用_root_,因为,所有的顶层包都被当做_root_的成员,写法如:
gyt 与 gyt3 都属于各自文件的顶层包时:
在这里插入图片描述

3. 引入

引入关键字import,被引入的包和它的成员可以简单使用,而不需要限定名称。
准备一个待引入的包:

package gyt
abstract class Fruit(
  val name: String,
  val color: String
)
object Fruits {
  object Apple extends Fruit("apple", "red")
  object Orange extends Fruit("orange", "orange")
  object Pear extends Fruit("pear", "yellowish")
  val menu = List(Apple, Orange, Pear)
}

引入语句示例:

// 引入Fruit
import gyt.Fruit

// 引入gyt的所有成员
import gyt._

// 引入Fruits的所有成员
import gyt.Fruits._

另外,Scala的引入可以出现在代码的任何地方。

Scala的灵活引入:

与Java相比,Scala的import 子句要灵活得多。主要的区别有三点:
在Scala中,引入可以:

  • 出现在任何位置
  • 引用对象(不论是单例还是常规对象),而不只是包
  • 让你重命名并隐藏某些被引入的成员

Scala引入可以重命名或隐藏指定的成员。做法是包括在花括号内的引入选择器子句中。如:

// 只引入Fruits对象中的Apple和Orange
import Fruits.{Apple, Orange}

// 只引入Fruits对象中的Apple和Orange, 并对Apple重命名为NewAp
import Fruits.{Apple=>NewAp, Orange}

// 引入Fruits的所有成员, 相当于import Fruits._
import Fruits.{_}

// 引入Fruits的所有成员, 并对Apple重命名为NewAp
import Fruits.{Apple=>NewAp,_}

// 引入Fruits的所有成员, 除了Apple
import Fruits.{Apple=>_,_}

因此,可以总结引入选择器:

  • 一个简答的x。
  • 一个重命名x=>y。
  • 一个隐藏子句x=>_。
  • 捕获所有x=>_。

4. 隐式引入

Scala对每个程序都隐式地添加了一些引入。每个".scala"源文件的顶部都添加了如下三个引入子句:

import java.lang._
import scala._
import Predef._

所以,能够直接使用java.lang、scala、Predef 包中的对象。
注意,引入的顺序,如果出现同名的对象,后面的引入会遮挡前面的引入。 就像内部的同名变量会遮挡外部的同名变量。

5. 访问修饰符

Java中有四种访问修饰符,而Scala中有三种:private、protected、公共的。Scala与Java大体保持一致,但也有一些重要区别。

  • 私有成员
    私有成员只在含定义的类或对象的内部可见,与Java相同。但是,有一个区别,如:
class Outer {
  class Inner {
    private def f() = println("hello")
    class InnerMore {
      f() // OK
    }
  }
  (new Inner).f() //错误, 无法访问
}

也就是说,Scala无法从外部类访问内部类的私有成员,而Java可以。

  • 受保护的成员
    更Java相比,Scala对proteced成员的访问也更加严格。在Scala中,protected的成员只能从定义该成员的子类访问。而Java允许同一个包内的其他类访问这个类的受保护的成员。
    Scala提供了另一种方式到达这个效果,即:修饰符[限定词]
    如:
class father {
  protected def f() = println("hello")
}
class sun extends father {
  f() // OK
}
class other {
  f() // 错误
}
  • 公共成员
    Scala没有专门的修饰符修饰公共成员:任何没有被标记为private和protected的成员都是公共的。公共成员可以从任何位置访问。

前面提到了,Scala可以使用修饰符[限定词] 的格式更加细粒度的修饰成员。
例如:
源代码:

解释:
在这里插入图片描述
值得一提的是,private[this]的定义,只能在包含该定义的同一个对象访问。该定义称为对象私有
也就是说,对它的访问必须来自对象内部,并且为同一个实例。
如果不是同一个实例,则访问不被允许,如:

// speed 的修饰符为private[this]
val other = new Navigator
other.speed	// 不能被编译, 不是同一个实例
可见性和伴生对象

在Java中,静态成员和实例成员同属于一个类,因此访问修饰符对它们的应用方式是统一的。
Scala没有静态成员,而使用伴生对象来承载只存在一次的成员。
伴生类和伴生对象无论各自的成员是公共的、protected、private都可以互相访问。
不过,伴生对象中,protected 成员没有意义,因为伴生对象没有子类。

6. 包对象

目前为止,能够往包中添加类、特质、孤立对象等。这些都是放在包内顶层最常见的定义。任何你能够放在类级别的定义,都能够放在包级别。如在包级别放一个函数,作为整个包都可以使用的助手函数。
具体的做法是把定义放在包对象。每个包都允许一个包对象,任何放在包对象的定义都会被当作包本身的成员。
示例:
gyt/package.scala
注意:package + object

package object gyt {
  def f() = {
    println("hello")
  }
}

gyt/Test.scala

package gyt

object Test {
  def main(args: Array[String]): Unit = {
    f()
  }
}

包对象会被编译成名为package.class的类文件,所以,该文件位于它增强的包的对象的目录下。习惯将包对象的源代码放在包名目录下的package.scala中。
运行:
需要构建一个Scala项目,这里使用IDEA。
在这里插入图片描述

后续篇章

12-样例类和模式匹配

完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值