Scala学习笔记(3): 面向对象与容器

转载 2016年05月31日 10:44:41

面向对象编程

这个通过一个例子来解释Scala面向对象编程的基本概念,这里我们需要为MongoDB(一种NoSQL数据库,详见我的另一篇博客)编写一个简单的API。MongoDB官方提供了Java API,而我们要做的是使用Scala对其进行简单的封装。

Class

先来搭建一个叫MongoClient的类作为建立数据库连接的入口,定义一个MongoClient类,并指定主构造函数的两个参数(val host: String, val port: Int)。

class MongoClient(val host: String, val port: Int)

构造函数参数的格式可以是[val/var] param : type,区别在于:val参数是只读的(相当于只有get方法),var参数是可写的(有get和set方法),如果没有前缀则是私有的(没有get和set方法)

构造函数

可以在类中定义多个构造函数(包括默认构造函数),例如下面是MongoClient的一个完整定义:

class MongoClient(val host: String, val port: Int) {
  
  require(host != null, "You have to provide a host name")
  private val underlying = new Mongo(host, port)

  def this() = this("127.0.0.1", 27017)
  def version = underlying.getVersion()
  def dropDB(name: String) = underlying.dropDatabase(name)
  def createDB(name: String) = DB(underlying.getDB(name))
  def db(name: String) = DB(underlying.getDB(name))
}

def this() 定义了一个默认构造函数。

Case class

case class是一种特殊的Class,编译器会为其自动生成一些样板代码(equals, hashCode, toString, apply等)。

case class Person(firstName:String, lastName:String)

Object

Scala中不存在静态变量和静态方法,所有的变量和方法必须定义在类或者定义在object中,所谓的object就是一个单例对象。

object可以用来定义一个可执行类:

object HelloWrold extends App {
    println("Hello World")
}

object可以定义

object的另一种常见的用法是作为一个类的伴随对象,用来定义一些“静态”的工厂函数。例如下面这段程序中,我们首先定义了DB类,然后定义了一个同样叫DB的伴随对象,伴随对象可以可以访问伴随类的私有成员。

class DB private (val underlying: MongoDB){ //注意private关键词,DB的构造函数是私有的
  def collectionNames = for(name <- new JSetWrapper(underlying.getCollectionNames())) yield name
  
  private def collection(name: String) = underlying.getCollection(name)
  
  def readOnlyCollection(name: String) = new DBCollection(collection(name))
  def administrableCollection(name: String) = new DBCollection(collection(name)) with Administrable
  def updatableCollection(name: String) = new DBCollection(collection(name)) with Updatable 
}

object DB {
  def apply(underlying: MongoDB) = new DB(underlying)
}

包与引入

Scala中包也是一种特殊的对象,在包内部可以看定义其他类与对象,如果要在包里面定义函数或者变量的话,请使用包对象:

//package.scala
package object bar {
  val minimumAge = 18
  def verifyAge = {}
}

//other.scala
package bar
class BarTender {
  def serveDrinks = { verifyAge; ... }
}

包的定义可以是嵌套式的(C#中常见),或者是平板式的(Java中常见)。引用包的常见写法有:

import com.mongodb.DBObject
import com.mongodb._  //_与Java中的×(星号)是一个意思
import com.mongodb.{DBCollection => MongoDBCollection} //引入包后更改其名称

Scala的继承模型

scala.Any是任何类的父类;Scala.AnyRef相当于Java中的Object类,即任何引用类的父类;AnyVal是任何值类的父类。

在图中实线表示继承关系,虚线表示view关系,view的意思是类型间存在隐式转换。

scala class.png

在类层级的最底下是Scala.Null和Scala.Nothing,Scala.Null类型只有一个实例化对象null, Scala.Nothing则不能实例化。

特质

Trait可以翻译成特质,你可以认为特质是一种增强版的接口,但我认为把它理解为一种部分实现的类更为合适。为什么这么说?因为使用特质构造程序,允许我们把一个大的类打散成多个特质,相当于把机器拆成零件,然后再自由组装。

特性 Interface (Java) Abstract class (Java + Scala) Trait
实例化 No No No
构造参数 No Yes No
方法定义 Yes Yes Yes
方法实现 No Yes Yes
成员变量 No Yes Yes
多重组合(继承) Yes No Yes

例如下面这个例子中,我们把mongodb java API的功能打散成几个不同的特质,所以特质都从ReadOnly特质继承而来。

trait ReadOnly {
  val underlying : MongoDBCollection
  
  def name = underlying getName
  def fullName = underlying getFullName
  def find(doc: DBObject) = underlying find doc
  def findOne(doc: DBObject) = underlying findOne doc
  def findONe = underlying findOne
  def getCount(doc: DBObject) = underlying getCount doc
  
  def find(query: Query): DBCursor = {
    def applyOptions(cursor:DBCursor, option: QueryOption): DBCursor = {
    option match {
      case Skip(skip, next) => applyOptions(cursor.skip(skip), next)
      case Sort(sorting, next)=> applyOptions(cursor.sort(sorting), next)
      case Limit(limit, next) => applyOptions(cursor.limit(limit), next)
      case NoOption => cursor
    }
    }
    applyOptions(find(query.q), query.option)
  }
}

trait Administrable extends ReadOnly {
  
  def drop: Unit = underlying drop
  def dropIndexes : Unit = underlying dropIndexes
  
}

trait Updatable extends ReadOnly {
  def -=(doc: DBObject): Unit = underlying remove doc
  def +=(doc: DBObject): Unit = underlying save doc
}

trait LocaleAware extends ReadOnly {
  override def findOne(doc: DBObject) = {
    doc.put("locale", java.util.Locale.getDefault.getLanguage)
    super.findOne(doc)
  }
  override def find(doc: DBObject) = {
    doc.put("locale", java.util.Locale.getDefault.getLanguage)
    super.find(doc)
  }
}

class DBCollection(override val underlying: MongoDBCollection) extends ReadOnly

//用法
new DBCollection(mongoDBCollection) with Updatable with Administrable

Scala容器

Scala中容器被划分为两类:immutable和mutable,分别属于包 scala.collection.immutable和 scala.collection.mutable。Java程序员可以用String和StringBuffer的来理解它们的区别,即immutable容器上的增删操作会返回一个新的immutable容器,而mutable容器上的增删操作是in place(会产生副作用)。

所有immutable和mutable容器都继承自scala.collection里定义的抽象类,它的类结构如下:

scala-collection.png

  • IndexedSeq的实现是Vector, LinearSeq的实现是list
  • 通过使用隐式转换,Java中的String和Array类型在Scala中“继承”了IndexedSeq接口

常见的collection操作

  • map 使用函数f(T=>T)将collection中的元素映射到一个新的collection
  • flatMap 函数映射f(T=>List[T]),但会把collection扁平化
  • filter
  • foldLeft
  • foldRight

并发容器

Scala 2.9引入了并发容器,使得很多容器操作可以安全的并发执行。例如,在普通容器类后加.par后缀即可转换为并发容器。

myCollection.par.foldLeft(0)((a,b) => a+b)

Par容器的内部实现是基于类似于MergeSort的分割组合算法。


相关文章推荐

闭包 Scala学习笔记-面向对象篇

以前一直不知道什么叫闭包,网上找了半天也没明白,只知道闭包的定义: 代码+非局部变量 = 闭包代码好理解,那什么时非局部变量呢? 首先我们定义一个种树的方法:def plantATree(treeN...

scala 学习笔记 面向对象

4.   OOP 4.1.     类class 4.1.1.  定义 例子1: class User {   var name = "anonymous"   v...

Scala学习笔记之面向对象

创建对象 val greetStrings = new Array[String](3) 如果类还实现了apply方法,那么可以简写 val greetStrings = Array[String]...
  • ssjssh
  • ssjssh
  • 2013年09月28日 12:46
  • 3688

Java学习笔记(5):3.面向对象之方法详解

方法是类或对象的行为特征的抽象,方法是类或对象最重要的组成成分。但从功能上看,方法完全类似于传统结构化程序设计里的函数。值得指出的是,Java里的方法不能独立存在,所有的方法必须定义在类里。方法在逻辑...
  • June94
  • June94
  • 2015年12月23日 22:44
  • 356

Java学习笔记<3>面向对象相关

面向对象的基本思想
  • wxwd1
  • wxwd1
  • 2014年08月14日 18:15
  • 486

java学习笔记-------2011/3/12 面向对象(一)

      封装,即数据隐藏,它将数据和行为组合在一起,并对对象隐藏了数据的实现方式。对象中的数据又称为实例域,操纵这些数据的过程是方法,对于每一个特定的对象都有一组属于他自己的实例域值,这些值的集合...
  • ynu2010
  • ynu2010
  • 2011年03月12日 20:36
  • 152

java学习笔记-------2011/3/14 面向对象(二)

     在编写java类的时候应该注意一下几点:     (1)一般来说,对于数据域,我们一般将其设置成私有的,然后设置域访问器(get)和域更改器(set)。并非所有的数据域都应该设置域更改器,有...
  • ynu2010
  • ynu2010
  • 2011年03月14日 19:40
  • 113

JavaScript入门学习笔记(3)—— 面向对象

创建对象工厂模式function createPerson(name, age, job) { var o = new Object( ); o.name = name; o....
  • Joy_fun
  • Joy_fun
  • 2016年08月03日 10:27
  • 93

Lua学习笔记Day3-Lua实现类、Lua面向对象

Lua学习笔记Day3-Lua实现类、Lua面向对象Lua学习笔记Day3-Lua实现类Lua面向对象 目录 Lua实现类目录 Lua实现类 Lua实现类 Lua实现类 创建一个人类Perso...
  • Teddy_k
  • Teddy_k
  • 2016年05月16日 22:50
  • 127

java学习笔记(3)——面向对象

this关键字 this是当前对象的引用,是运行期间当前对象本身。 可以使用this明确的访问当前对象的属性或者方法,类似于“我” this()可以调用本类的其他构造器,可以使用构造器的重用简化...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Scala学习笔记(3): 面向对象与容器
举报原因:
原因补充:

(最多只允许输入30个字)