Scala -Packages and Imports

Scala has features with the well-defined module organization methods, and it uses the similar organization methods as that in the java but with some differences. The differences lies the visibility and scope rules. Instead of enforcing the one file one package rule abided by Java programs, it has less strict and gives the programmer the ability to organize the module in either C# namespace like style or more tranditional java style (but with ability to nest packages). THe scala imports also empowers the user to selectively choose symbols, to hide or rename imports.  


in this post , we will see how we can leverage the scala pacakegs and how we can use te imports  construct to import useful symbols, and how scala organize around the visiblity ..

things that are special in Scala includes the following
multiple package allowed

  • C# style nesting package and java style package declaration (implicit package enabled)
  • automatic visibility with Scala pacakge (Concise acccess to related code)
  • Imports
  •      import a single symbol
  •      import wildcards
  •      Import with rename
  •      import hide
  •      impliicit import (those pacakge that shall be imported automatically)
  • Access control
  •      Access modifier
  •      private members
  •      proteced members
  •      public members
  • Scope of protection (those private[bobsrockets])
  • visibility and companion object
  • package objects

there are two types of pacakge declaration available in the scala programming language. they are 

// all content in a single package
package bobsrockets.navigation
class Navigator
and it also support the c# style package.
// content in the curly braces are in one packkage. 
package bobsrockets.navigation {
  class Navigator
}

and scala permits more flexible package (you can treat it as namespace as well) 

// Scala's style can be extremely flexible 
package bobsrockets {
  package navigation { 
    class Navigator { 
      
    }
    
    package tests {
      class NavigatorSuite
    }
  }
}

Concise access to related code. 


the purpose of using package to organize the code is that it allows to gain some access to symbols with certain rules (related to containing package, the same package, packaged in further contained packages), which gives ppl who are working on the same functionality (usually they will work on the same package or related packages.) 

We will check on this with some examples. 

package bobsrockets { 
  package navigation{ 
    class Color {}
  }
}

package bobsrockets {
  
  package navigation { 
    class Navigator {
      val map = new StarMap 
    }
    class StarMap
  }
  
  class Ship {
    // No need to say bobsrockets.navigatoin.Navigator
    val nav = new navigation.Navigator
  }
  
  package fleets { 
    class Fleet { 
      // no need to say bobsrockets.Ship
      def addShip() { new Ship}
    }
  }
}

if a package is nested in this manner (explicitly nested), can be accessed by packages containing this package (navigation.Navigator), and names accessible in scopes outside the package can be accessed also inside (the addShip call) and one important rule of all, names in the same packages can be accessible from names inside the same package.)

But this rule maybe might not hold if one package per file is used, an example is as follow.

// in Ship.scala
package bobsrockets {
  class Ship
}

// in Fleet.scala
package bobsrockets.fleets {
  class Fleet { 
    // Doesn't compile ! Ship is not in scope
    def addShip( new Ship)
  }
}
because according to java rule, only names from the same package are accessible to each other (while bobsrockets and bobsrockets.fleets are two separate package).

However sometimes, complication arises, when some local names might hide the outer ones, then you might need some means to refer from the global (root)...
// hidden_package.scala

// this shows how to access to names with qualified names and the use of _root_ (the global::)
package launch { 
  class Booster3
}

package bobsrockets { 
  package navigation { 
    package launch {
      class Booster1
    }
    class MissionControl { 
      val booster1 = new launch.Booster1
      val booster2 = new bobsrockets.launch.Booster2
      val booster3 = new _root_.launch.Booster3
    }
  }
  package launch { 
    class Booster2
  }
}
as you can see with booster1 is accessible with the short form, because the package rule allows non-root qualified names, while the second booster2 is not trick neither, because you can access it from the outer scope ,but the third one - booster3, which is difficult, because you can access the nested one with the following one bobsrockets.navigation.launch, but if you write launch.Booster3, intending to access the outer one, but the implicit rule also exists, and it is conflicting to tell which one you intend. so Scala provided a root package , which is outer side any package, so that you can write something like "_root_.launch.Booster" now refers explicitly the absolute name.

Imports

import is the way to introduce names without using its verbose/lengthy full qualified, 

we shall show this in examples. below is a Fruit.scala file. 

// fruits.scala
package bobsdelights 

abstract class Fruit (
  val name: String,
  val color: String
)

object Fruit { 
  object Apple extends Fruit("Apple", "red")
  object Orange extends Fruit("Orange", "orange")
  object Pear extends Fruit("Pear", "yellowish")
  val menu  = List (Apple, Orange, Pear)
}
and the following imports clause.
// scala_imports.scala

// easy access to Fruit 
import bobsdelights.Fruit
// easy access to all members of bobsdelights
import bobsdelights._
// eay access to all members of Fruits
import bobsdelights.Fruits._


// import can appear anywhere
// import can refer to arbitrary value
object A {
  
def showFruit(fruit : Fruit) {
  import fruit._
  println(name + "s are " + color)
}

  def main(args : Array[String]) {
    showFruit(menu(0))
  }
}
the reason to use '_' instead of '*' is because '*' is a valid identifiers, 

One important difference is that the Scala imports are more general, in that (1) it can appear anywhere and (2) it can import arbitrary values. as you can see in the above code, where we import members of a parameter "fruit". so that "color and others" can be acces without the "fruit.color" which is unimaginable in another language. 
let's present the flexible scala imports in bullet-points.

  • may appear anywhere in the code
  • May refer to objects (singleton or regular) to additional to package.
  • let you rename and hide some of the important members. 

another rule onthe imports is that scala allows  you to import the packages themeselves., see the following. 

// file:
//  import_scala_package_as_name.scala
// unlike the java package import, the scala import rule allow you to import package themselves, 
// as opposed to the java package, where only the package member are allowed to be imported

// 
import java.util.regex

class AStarB { 
  // Accesses java.util.regex.Pattern 
  val pat = regex.Pattern.compile("a*b") // regex now becomes a symbol. much like the C# style. 
}
import selector

import selector allows you to hide/rename import members. we wil give a tour on the import selector as below.

// file:
//   import_selector.scala

// this will only show code samples, it may not have any real meaning. 
//import Apple adn Orange the basic simple form 
import Fruits.{Apple, Orange}

// import Apple and Orange and rename Apple to McIntosh 
import Frtuis.{Apple => McIntosh, Orange}

// import rename work on package members
import java.sql.{Date => SDate}

// and it also works on package it self
import java.{sql => S} // now you can use S.Date

// import every thing
import Fruits.{_}
// is the same as saying
import Fruits._

// now with wildcards, we can do more
// Import everything but rename Apple to McIntosh
import Fruits.{Apple => McIntosh, _}

// with wildcards, we can do name hiding
// import everything except Pear
import Fruits.{Pear => _,  _}

As you have seen, you see curly bracket as a basic selector form, and you see => as a way to rename members, and you see => _ as a mnemonic of hiding something.

so import selector consists of the following. 

  1. renaming clause x => y
  2. hiding clause x => _
  3. catch-all clause _

implicit import

java implicitly import java.lang.*, scala has implicit import as well, they are
// by default, the following are imported

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

Access Modifier

Access modifiers control the visibility of names explicitly. normally you will use private or protected (public is by default in scala?). This modifier restrict accesses to the members to certain regions. 

private and protected in scala is more restricted than in Java, in java, you can access private member of inner classes but you cannot do that in Scala. similarily, in java, protected member can be accessed from the classes in the same package, but scala does not allow it.

e.g.

// private are more restrictive

class Outer {
  class Inner { 
    private def f() { 
      
    }
    
    class InnerMost { 
      f() // OK 
    }
  }
  (new Inner).f() // does not compile
}


// protected are more restrictive
package p {
  
  class Super { 
    protected def f() { println("f")}
  }
  
  class Sub extends Super { 
    f()
  }
  
  class Other { 
    (new Super).f() // does not compile
  }
}

what is more special about the Scala's access rule is the scope of protection

Scope of Protection

Access modifier in Scala can be augumented with qualifiers. A modifier of the form private[X] or protected[X] means that access is private or protected "up to" x ... X can be some enclosing package, class or singleton object 

here is an example in code. 

package bobsrockets 
package navigation {  
  // an earlier example of scope of protection
  private[bobsrockets] class Navigator { 
    protected[navigation] def useStarChart() { 
      
    }
    class LegOfJourney { 
      private [Navigator] var distance = 100
    }
    // private[this] , only this object can access the 
    private[this] var speed = 200
  }
  
}
package launch {
  import navigation._
  object Vehicle { 
    private[launch] val guide  = new Navigator
  }
}
actually we have a table, giving that we are talking about private on the symbol LegOfJourney.Distance

no access modifier 
public acceess
private[bobsrockets] 
access wihtin outer package
private[navigation]
same as package visibility in Java
private[Navigator] 
same as private in java
private[LegOfJourney] 
same as private in scala
private[this]
access only from the same object

there is also   protected[X],  which means both the extended classes and also within the enclosing scope package, class, or object X; 

one special point is the there is a special instance accessibility qualifier, which is private[this], this means it only able to accessed from the same object. given an example as below. 

// instance access qualifier e.g.
// inside the Nvigator class somewhere... 
val other = new Navigator
other.speed // this won't copmile..

Visibility and Companion object

Scala does not have static members, as java does, but it does have companion object, 

Scala's access rules priviledge companion object and classes when it comes to private or public accesses.
A classes shares all its access right s with its companion objects and vice versa. here is an example. 

// Companion object
class Rocket {  
  import Rocket.fuel
  // access the companion object
  private def CanGoHomeAgain = fuel > 20 
}


object Rocket {
  private def fuel = 10
  def chooseStrategy(rocket : Rocket ) {
    // access the companion class 
    if (rocket.CanGoHomeAgain)
      goHome()
    else 
      pickAStar()
  }
  def goHome() {}
  def pickAStar() {}
}

Package object

One things special about scala package is that Scala package itself is an object (this sound rather wierd at first sight, but you will slowly get the rational behind it) . 

so far you have seen classes, traits, and standalone object placed inside package, scala does not limit you to this, if you have some helper methods that you want to be in scope for an entire package.  go ahead and put it right at the top level of the packages.

To do so, put the definition in a "package object". Only one package object is allowed per package. Definitions inside the package object are considered members of the package itself.
e.g.

// Companion object
class Rocket {  
  import Rocket.fuel
  // access the companion object
  private def CanGoHomeAgain = fuel > 20 
}


object Rocket {
  private def fuel = 10
  def chooseStrategy(rocket : Rocket ) {
    // access the companion class 
    if (rocket.CanGoHomeAgain)
      goHome()
    else 
      pickAStar()
  }
  def goHome() {}
  def pickAStar() {}
}
as you will find later, that the package object is lays the foundation for implicit conversion (like string to StringOp conversion) and many others. 

转载于:https://my.oschina.net/u/854138/blog/132794

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值