Facebook Twitter LinkedIn E-mail
magnify
Home 2014 三月

Scala开发入门教程

Scala语言和其它语言比如Java相比,算是一个比较复杂的语言,它是一个面向对象和面向函数的统一体,使用起来非常灵活,因此也不容易掌握,前面的58篇文章仅仅是Scala的一个入门教程,有了这些基础知识,你就可以开始编写Scala应用,学习一种语言最好的方法是通过实践来学习。为方便起见,下面列出这些文章,后面我们就分专题进一步介绍Scala编程。

  1. Scala开发教程(1): 开始神奇的Scala编程之旅
  2. Scala开发教程(2): 起步Scala
  3. Scala开发教程(3): 进一步Scala
  4. Scala开发教程(4): 类和对象 (一)
  5. Scala开发教程(5): 类和对象 (二)
  6. Scala开发教程(6): 基本数据类型
  7. Scala开发教程(7): 操作基本数据类型
  8. Scala开发教程(8): 类和对象 (三)
  9. Scala开发教程(9): 类和对象 (四)
  10. Scala开发教程(10): 类和对象 (五)
  11. Scala开发教程(11): if 表达式
  12. Scala开发教程(12): while 循环
  13. Scala开发教程(13): for 表达式
  14. Scala开发教程(14): try表达式处理异常
  15. Scala开发教程(15): Match表达式
  16. Scala开发教程(16): 没有“break”和“continue”的日子
  17. Scala开发教程(17): 函数–类成员函数
  18. Scala开发教程(18): 函数–局部函数
  19. Scala开发教程(19): 函数–头等公民
  20. Scala开发教程(20): 函数–函数字面量的一些简化写法
  21. Scala开发教程(21): 函数–部分应用的函数
  22. Scala开发教程(22): 函数–闭包
  23. Scala开发教程(23): 函数–可变参数,命名参数,缺省参数
  24. Scala开发教程(24): 函数–尾递归
  25. Scala开发教程(25): 减低代码重复
  26. Scala开发教程(26): 柯里化函数
  27. Scala开发教程(27): 创建新的控制结构
  28. Scala开发教程(28): 传名参数
  29. Scala开发教程(29): 组合和继承–概述
  30. Scala开发教程(30): 组合和继承–抽象类
  31. Scala开发教程(31): 组合和继承–定义无参数方法
  32. Scala开发教程(32): 组合和继承–扩展类
  33. Scala开发教程(33): 组合和继承–重载成员函数和方法
  34. Scala开发教程(34): 组合和继承–定义参数化成员变量
  35. Scala开发教程(35): 组合和继承–调用基类构造函数
  36. Scala开发教程(36): 组合和继承–使用override修饰符
  37. Scala开发教程(37): 组合和继承–多态和动态绑定
  38. Scala开发教程(38): 组合和继承–定义final成员
  39. Scala开发教程(39): 组合和继承–使用组合还是继承?
  40. Scala开发教程(40): 组合和继承–实现类Element的above,beside和toString()方法
  41. Scala开发教程(41): 组合和继承–定义factory对象
  42. Scala开发教程(42): 组合和继承–定义heighten和widen函数
  43. Scala开发教程(43): 组合和继承–小结
  44. Scala开发教程(44): Scala的类层次关系
  45. Scala开发教程(45): Scala基本数据类型的实现方法
  46. Scala开发教程(46): 所有类的公共子类–底层类型
  47. Scala开发教程(47): Trait的基本概念
  48. Scala开发教程(48): 选择瘦接口还是胖接口设计?
  49. Scala开发教程(49): Trait示例–Rectangular 对象
  50. Scala开发教程(50): Ordered Trait
  51. Scala开发教程(51): Trait用来实现可叠加的修改操作
  52. Scala开发教程(52): 使用Package–将代码放入包中
  53. Scala开发教程(53): 引用包中的代码
  54. Scala开发教程(54): 使用import
  55. Scala开发教程(55): 隐含的import
  56. Scala开发教程(56): 访问控制修饰符
  57. Scala开发教程(57): 为访问控制修饰符添加作用域
  58. Scala开发教程(58): 包对象
 

Scala开发教程(58): 包对象

到目前为止,我们看到的添加到包的都是类型,Trait和单例对象(Object)。这些都是指包的定级层次定义的类型。Scala的定级层次除了可以定义类,Trait,Object之外,其它可以在类,Trait,Object内部定义的类型,也都可以直接定义在包中,比如一些通用的函数,变量,你都可以直接定义在包中。
在Scala中,可以把这些函数或方法放在一个称为“包对象”中。每个包只有一个包对象,任何放在包对象的类型都可以认为是包自身的成员。
例如:

//in file bobsdelights/package.scala

package object bobsdelights{
  def showFruit(fruit: Fruit){
    import fruit._
    println(name + "s are " + color)
  }
}

//in file PrintMenu.scala

package printmenu
import bobsdelights.Fruits
import bobsdelights.showFruit

object PrintMenu{
  def main(args:Array[String]){
    for(fruit <- Fruits.menu){
      showFruit(fruit)
    }
  }
}

本例中对象PrintMenu可以引入包对象中定义的函数showFruit,方法和引入一个类定义一样,也是通过import 语句。
包对象通常被编译为package.class,其包名为定义的包。所有按照惯例一般包对象定义放在package.scala中,比如上面的包对象可以放在bobsdelights目录下的package.scala中。

 

Scala开发教程(57): 为访问控制修饰符添加作用域

Scala的访问修饰符可以添加作用域参数。作用域的语法如下:
private[x]或protected[x]
其中x代表某个包,类或者对象,表示可以访问这个Private或的protected的范围直到X。

通过为访问修饰符添加作用域参数,可以非常精确的控制所定义的类型能够被其它类型访问的范围。尤其是可以支持Java语言支持的package private, package protected等效果。

下面的例子为这种用法的一个示例:

package bobsrockets

package navigation{
  private[bobsrockets] class Navigator{
    protected[navigation] def useStarChart(){}
    class LegOfJourney{
        private[Navigator]  val distance=100
      }

    private[this] var speed = 200



    }
  }

  package launch{
    import navigation._
    object Vehicle{
      private[launch] val guide=new Navigator
    }
  
}

在这个例子中,类Navigator使用private[bobsrockets]来修饰,这表示这个类可以被bobsrockets中所有类型访问,比如通常情况下Vehicle无法访问私有类型Navigator,但使用包作用域之后,Vechile中可以访问Navigator.

这种技巧在分散在多个Package的大型项目时非常有用,它允许你定义一些在多个子包中可以访问,但对使用这些API的外部客户代码隐藏,而这种效果在Java中是无法实现的。

此外,Scala还支持一种比private还要严格的访问控制,本例中的private[this],只允许在定义该成员的类型中访问,它表示该成员不仅仅只能在定义该成员的类型中访问,而且只能是由该类型本身访问。比如:
本例中speed,使用 protected[this], speed,和this.speed只在定义该成员的实例中可以访问,下面的用法也是不合法的,即使它们也在Navigator里面。当由于是新创建的另外的实例,编译出错:

20140324001

 

Scala开发教程(56): 访问控制修饰符

包的成员,类或对象可以使用访问控制修饰符,比如private和protected来修饰,通过这些修饰符可以控制其他部分对这些类,对象的访问。Scala和访问控制大体上和Java类似,但也有些重要的不同,本篇将介绍这些。
私有成员
Scala的私有成员和Java类似,一个使用private修饰过的类或对象成员,只能在该类或对象中访问,在Scala中,也可以在嵌套的类或对象中使用。比如:

class Outer{
  class Inner{
    private def f(){
      println("f")
    }
    
    class InnerMost{
      f() //OK
    }
  }
  
  (new Inner).f();// error: f is not accessible
}

在Scala中,(new Inner).f()是不合法的,因为它是在Inner中定义的私有类型,而在InnerMost中访问f却是合法的,这是因为InnerMost是包含在Inner的定义中(子嵌套类型)。 在Java语言中,两种访问都是可以的。Java允许外部类型访问其包含的嵌套类型的私有成员。

保护成员
和私有成员类似,Scala的访问控制比Java来说也是稍显严格些。在Scala中,由Protected定义的成员只能由定义该成员和其派生类型访问。而在Java中,由Protected定义的成员可以由同一个包中的其它类型访问。在Scala中,可以通过其它方式来实现这种功能。
下面为protected的一个例子:

class p{
  class Super{
    protected def f() {
      println("f")
    }
  }
  
  class Sub extends Super{
    f()
  }
  
  class Other{
    (new Super).f() //error: f is not accessible
  }
}

公开成员
public访问控制为Scala定义的缺省方式,所有没有使用private和protected修饰的成员都是“公开的”,可以被自由访问。Scala不需要使用public来指定“公开访问”修饰符。

 

Scala开发教程(55): 隐含的import

Scala缺省为每个文件添加如下几个package. 这几个包无需明确指明。


import java.lang._   //everything in the java.lang package
import scala._       //everything in the scala package
import Predef._      //everything in the Predef object

因此在写Scala应用之前,先了解下这些缺省包定义了那些类和功能。

此外这三个包的顺序也需要了解一下,比如StringBuilder类定义在包scala 和java.lang包中,后定义的import会覆盖前面的定义,因此如果不明确指明,
StringBuilder为scala.StringBuilder而非java.lang.StringBuilder.

注意这里的scala._ 指所有scala下的包,包括子包,也就是所有http://www.scala-lang.org/files/archive/api/2.10.3/#package

Predef为一对象(非报名),因此可以直接使用Predef对象定义的方法(静态引用)。因此在写代码之前了解Scala包和Predef定义的功能尤其重要.

201403180001

 

Scala开发教程(54): 使用import

和Java一样,Scala也是通过import语句引用其它包中定义的类型,类型引入后,可以使用短名称来引用该类型而无需使用全路径。要注意的Scala使用“_” 而非”*”作为通配符。

//easy access to Fruit
import bobsdelights.Fruit

//easy access to all members of bobdelights
import bobsdelights._

//easy access to all member of Fruits
import bobsdelights.Fruits._

所定义的类型中包bobsdelights中:

package bobsdelights

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)
}

第一个为引用单个类型,第二个为按需引用,和Java不同的是,是使用“_”代替“*”,第三个类似于Java中的静态引用,可以直接使用Fruits中定义的对象。

此外Scala中的import语句的使用比较灵感,可以用在代码的任意部分,而不一定需要在文件开头定义。比如下面 import定义在函数内部:

import bobsdelights.Fruit

def showFruit(fruit:Fruit){
  import fruit._

  println(name+"s are" + color)
}

方法showFruit 引入fruit对象(非类型)的所有成员,fruit的类型为Fruit,因此可以在函数直接使用fruit的成员变量,而无需使用fruit限定符。这个方法和下面代码是等价的:

import bobsdelights.Fruit

def showFruit(fruit:Fruit){
   println(fruit.name+"s are" + fruit.color)
}

和Java相比,Scala的import的使用更加灵活:

  • 可以出现在文件中任何地方
  • 可以import对象(singleton或者普通对象)和package本身
  • 支持对引入的对象重命名或者隐藏某些类型

下面的例子直接引入包名称,而非包中成员,引入包后,可以使用相对名称来指代某个类型(有些类型文件系统的路径)

import java.util.regex

class AStarB {
  val pat= regex.Pattern.compile("a*b")
}

import 也可以用来重命名或者隐藏某些类型,比如:

import Fruits.{Apple,Orange}

仅仅引用Fruits中的Apple和Orangle类型。
下面的例子使用=>重命名类型:

import Fruits.{Apple=>MaIntosh,Orange}

同样重命名也可以重新定义包名称,比如

import java.{sql => S}

将引入的包java.sql 该名为 java.S 因此可以使用 S.Date 来代替 sql.Date

如果需要隐藏某个类型,可以使用 Type => _ ,将某个类型改名为_就达到隐藏某个类型的效果,比如

import Fruits.{Apple=>_,_}

这个引用,引入Fruits中除Apple之外的其它类型。