Scala


Classes

In Scala, we do this by defining a class :

class Rational(x:Int, y:Int){
	def numer = x;
	def denom = y

}

This definition introduces two entitiles:

  • A new type, name Rational.

  • A constructor Rational to create elements of this type.

Scala keeps the names of types and values in different namespaces

So there’s no confict between the two definitions of Rational

Objects

We call the elements of a class type objects.

We create an object by prefixing an application of the constructor of the class with the operator new.

  • Example:

new Rational(1,2)

Members of an Object

Objects of the class Rational have two members, numer and denom.

We select the members of an object with the infix operator’.’(like in Java).

  • Example

val x = new Rational(1,2)                 //> x  : week3.Rational = week3.Rational@2c641e9a
x.numer                                         //> res0: Int = 1
x.denom                                         //> res1: Int = 2


Methods

One can go further and also package functions operating on a data abstraction in the data abstraction itself.

Such functions are called methods.

  • Example

Rational numbers now would have, in addition to the functions numer and denom, the functions add,sub,mul,div,equal,toString


class Rational(x:Int, y:Int){

	def numer = x
	def denom = y

	def add(that:Rational) = new Rational(numer*that.denom + denom*that.numer,
	 denom*that.denom)
	 
	 def neg:Rational = new Rational(-numer,denom)
	 
	 def sub(that:Rational) = add(that.neg)
	 
	 override def toString = numer + "/" + denom
}

2-6: More Fun With Rationals

###Rationals with Data Abstraction


class Rational(x:Int, y:Int){

	def numer = x/g
	def denom = y/g
	
	private def gcd(a:Int, b:Int) : Int = if (b == 0) a else gcd(b,a%b)
	 
	private val g = gcd(x,y)
	 
	...
	
}

定义私有成员g和私有函数gcd

It is equally possible to turn number and denom into vals, so that they are computed only once:


class Rational(x:Int, y:Int){

	val numer = x/gcd(x,y)
	val denom = y/gcd(x,y)
	
	...
}

这种优化和改动都在这个类的里面,并不影响调用者,这种对数据的封装成为data abstraction

Self Reference

On the inside of a class, the name this represents the object on which the current method is executed.

  • Example:

class Rational(x:Int, y:Int){

...

def less(that:Rational) = numer * that.denom < that.numer * denom

def max(that:Rational) = if(this.less(that)) that else this

...	 
	 
}
	

Precondition

可以在类中加入对参数的合法性校验:


class Rational(x:Int, y:Int){

	require(y != 0, "denominator can not be 0")
	
	...

}

require is a predefined function.

It takes a condition and an optional message string.

if the condition is false than IllegalArgumentException is thrown with the given message string.

Assertions

Besides require, there is also assert

Assert also takes a condition and an optional message:


val x = sqrt(y)
assert(x >= 0)

和require一样,assert在条件为false的时候也会抛出异常,但是异常类型为AssertionError

assert和require的目的不同:

  • require is used to enforce a precondition on the caller of a function.

  • assert is used as to check the code of the function itself.

Constructor

默认情况下,Scala的类有默认的构造函数,如果想增加构造函数,需要将this作为函数使用:


class Rational(x:Int, y:Int){

	 require(y != 0, "denominator can not be 0")

	 def this(x: Int) = this (x,1)
	 
	 ...

}

val z = new Rational(3)

2-7: Evaluations and Operations

Classes and Substitutions

我们之前可以使用substitution规则去分析函数,这节将这个规则用于class object

  • Question: How is an instantiation of the class new C(e1,...,em)

  • Answer: The expression arguments e1,...,em 参数和函数函数一样求值,得到:new C(v1,...,vm)

我们现在定义这样一个类:

class C(x1,...,xm){ ... def f(y1,...,yn) = b ...}

where:

  • 类的构造函数入参为:x1,...,xn

  • 类中定义个了一个函数f,参数为y1,...,yn

  • Question: How is the following expression evaluated?

new C(v1,...,vm).f(w1,...,wn)

Operators

上面的例子中,求两个有理数的和,代码为x.add(y),这种没有x + y 直观,因此,scala提供一种方式使方法调用变的更直观:

它由两部分syntax构成:

  • Step1: Infix Notation:

Any method with a parameter can be used like an infix operator.

it is therefore possible to write:

r add s  	=> 		r.add(s)

r less s 	=> 		r.less(s)

r max s		=>		r.max(s)

  • Step2: 定义操作符

Scala可以重载操作符:


 def < (that:Rational) = numer * that.denom < that.numer * denom
 

调用变为:

x.less(y)  等价于 x < y

2-8: Class Hierarchies

Abstract Classes

Consider the task of writing a class for sets of integers with the following operations

定义一个可以表示整数集合的类


abstract class IntSet{

	def incl(x:Int):InSet
	def contains(x:Int):Boolean


}


抽象类:

IntSet is an abstract class.

抽象类可以定义接口,但不需要实现。 抽象类不可以直接用new创建

Class Extensions

Let’s consider implementing sets as binary trees.

There are two types of possible trees: a tree for the empty set, and a tree consisting of an integer and two sub-trees.

Here are their implementations


class Empty extends IntSet
{
	def contains(x:Int):Boolean = false
	
	def incl(x:Int):IntSet = new NonEmpty(x,new Empty, new Empty)

}

class NonEmpty(elem: Int, left: IntSet, right:IntSet) extends IntSet
{
	def contains(x:Int):Boolean = 
	
		if (x<elem) left.contains(x)
		else if (x>elem) right.contains(x)
		else true

	def incl(x:Int):IntSet = 
		
		if(x<elem) new NonEmpty(elem,left.incl(x), right)
		else if(x>elem) new NonEmpty(elem, left, right.incl(x))
		else this

}

###Base Classes and SubClasses

  • IntSet is called the superclass of Empty and NonEmpty.

  • Empty and NonEmpty are subclasses of IntSet.

  • In Scala, any user-defined class extends another class.

  • If no superclass is given , the standard class object in the Java package java.lang is assumed

  • The direct or indirect superclasses of a class C are called baseclasses of C

  • So, the base classes of NonEmpty are IntSet and Object

Implementing and Overriding

如果父类中的方法没有implementation,那么子类可直接实现,如果父类方法中有implementation,那么子类需要override:


abstract class Base
{
	def foo = 1
	
	def bar:Int
}

class Sub extends Base
{
	override def foo = 2
	
	
	def bar = 3

}

Object Definitions

上面的例子中,Empty类其实没有变化,但是每次都要重新创建,如果把它定义为一个单例,则能省去许多重复创建的instance,在Scala中,使用object:


object class Empty extends IntSet
{
	def contains(x:Int):Boolean = false
	
	def incl(x:Int):IntSet = new NonEmpty(x,Empty,Empty)
	
	override def toString = {"."}

}

  • This defines a singleton object named Empty.

  • No other Empty instances can be created.

  • Singleton objects are valeus , so Empty evaluates to itself

Programs

之前的代码都是使用REPL或workSheet运行,这一节将会创建一个Scala Object:


package week4

object week4 {

  def main(args:Array[String]) = println("hello")
 
}

Dynamic Binding

Object-oriented languages(including Scala) implement dynmaic method dispatch.

This means that the code invoked by a method call depends on the runtime type of the object that contains the method.

  • Example

Empty contains 1

-> [1/x][Empty/this] false

= false

Something to Ponder

Dynamic dispatch of methods is analogous to calls to higher-order functions.

  • Question:

Can we implement one concept in terms of the other?

  • Objects in terms of higher-order functions?

  • HOF in terms of objects?

How class are organized

默认import的包:

  • All members of package scala

  • All members of package java.lang

  • All members of the singleton object scala.Predef.

Here are the fully qualified names of some types and functions which you have seen so far:

Int 		=> scala.Int
Boolean		=> scala.Boolean
Obejct		=> java.lang.Object
require 	=> scala.predef.require
assert		=> scala.predef.assert

Traits

In Java, as well as in Scala, a class can only have one superclass.

But what if a class has several natural supertypes to which it conforms or from which it wants to inherit code?

Here, you could use traits.

A trait is declared like an abstract class, just with trait instead of abstract class.

Trait类似Java中的interface


trait Planar
{
	def height: Int
	def width : Int
	def surface = height*width
}

OBject只能继承一个class,但可以实现多个trait


class Square extends Shape with Plannar with Movable...

Traits resemble interfaces in Java, but are more powerful because they can contains fields and concrete methods.

On the other hand, traits cannot have parameters, only classes can

Trait中不能定义property

###Top Types

Scala有几个基本类型:

  • Any : the base type of all types, Method: ‘==’, ‘!=’, ‘equals’, ‘hashCode’, ‘toString’

  • AnyRef : The base type of all reference types; Alias of ‘java.lang.Object’

  • AnyVal : The base type of all primitive types.

###The Nothing Type

Nothing is at the bottom of Scala’s type hierarchy. It is a subtype of every other type.

There is no value of type Nothing.

Why is that useful?

  • To signal abnormal termination

  • As an element type of empty collections

###Exceptions

Scala’s exception handling is similar to Java’s

The expression:

def error(msg:String) = throw new Error(msg) 

###The Null Type

Every reference class type also has null as a value.

The type of null is Null .

Null is a subtype of every class that inherits from Object; it is incompatible with subtypes of AnyVal


val x = null //x:Null = null

val y:String = null // y:String = null

val z:Int = null //error: type mismatch



if (true) 1 else false                          //> res0: AnyVal = 1

if的第一个分支返回的类型为Int,第二个分支返回的类型为boolean,那么表达式的类型应该是他们共同的类型:AnyVal

Polymorphism

以构建一个Con-Lists为例,Con-List是一个immutable linked list

它可以通过两种方式构建:

  • Nil : 构建一个empty列表

  • Cons 一个包含一个element的cell和其余的list部分

Cons-Lists in Scala

Con-List可以通过如下数据结构标示:


trait IntList
{
  
}

class Cons(val head:Int, val tail:IntList) extends IntList
{
  

}

class Nil extends IntList
{
  
}

Cons的构造函数中参数用val声明了,这种情况相当于自动给成员变量赋值:


class Cons(_head:Int, _tail:IntList) extends IntList
{
  	val heal = _head;
  	val tail = _tail;
}

Type Parameters

仅仅定义int类型的list太单一了,希望将其扩展到double,string类型,这时候需要使用泛型:


trait List[T]
class Cons[T](val head:T, val tail:List[T]) extends List[T]
class Nil[T] extends List[T] 

定义trait:


trait List[T]
{
  def isEmpty:Boolean
  
  def head:T
  
  def tail:List[T]
  
}

定义Cons,Nil


class Cons[T](val head:T, val tail:List[T]) extends List[T]
{
  def isEmpty:Boolean = false

}

class Nil[T] extends List[T] 
{
	def isEmpty:Boolean = true
	
	def head:Nothing = throw new NoSuchElementException("Nil.head")
	
	def tail:Nothing = throw new NoSuchElementException("Nil.head")
}

###Generic Functions

和类一样,函数也支持泛型参数[T], 例如下面这个函数创建一个泛型List


def singleton[T](elem:T) = new Cons[T](elem, new Nil[T])

singleton[Int](1)
singleton[Boolean](true)

###Type Inference

事实上,编译器可以对参数类型进行推断,[T]可以省略:

singleton(1)
singleton(true)

Types and Evaluation

类型并不影响Scala进行参数替换,根据Substitution规则,参数被代入后,在执行表达式求值前,所有的类型都被remove掉。

这一特性叫做:type erasure

使用这一特性的语言有: Java,Scala,Haskell,ML,OCaml.

有些语言在runtime中仍然带着类型:C++,C#,F#

Polymorphism

这次来自希腊,意思是多种形式,In programming, 它表示:

  • 函数的参数可以是多种类型的

  • 某一种类型可以有很多子类型

我们目前已经看到过这两个特性:

  • 子类: 可以将一个父类型的指针指向一个子类型

  • 泛型: 函数参数和类的参数可以声明成泛型

###excise

已知一个list: List[T],要求返回第n个元素:


def nth[T](n:Int,xs:List[T]):T = 
	
	if (n==0) xs.head 
	else nth(n-1,xs.tail)

2-11 Functions as Objects

这个section讲object和function之间的关系:

###Functions as Objects

在Scala中,函数被认为是object:

例如A => B这种函数类型其实是scala.Function1[A,B]的缩写,定义为:


package scala

trait Function1[A,B]{

	def apply(x:A):B

}

所以,函数只是object的apply方法,同样的,对于多个函数入参的trait还有Function2,Function3,…

Expansion of Function Values

匿名函数如下:


(x:Int) => x*x

被展开如下:


{
	class AnonFun extends Function1[Int,Int]
	{
		def apply(x:Int):Int = x*x
	}
	
	new AnonFun
}

或者更简单的,使用匿名类:


new Function1[Int,Int]
{
	def apply(x:Int) = x*x
}

Expansion of Function Calls

这样的函数调用f(a,b)会扩展为:


f.apply(a,b)


val f = (x:Int) => x*x

f(7)

被扩展为:


val f = new Function1[Int,Int]{ def apply(x:Int) = x*x }
f.apply(7)

所以:Functions are just Objects

Functions and Methods

下面的方法:


def f(x:Int):Boolean = ...

由上一小节可知f本身并不是函数类型,而是一个对象的apply方法。

但是如果f被放到了等号右边,需要成为一个函数类型的时候,会自动转换:


(x:Int) => f(x)

上面的匿名函数再展开:


new Function1[Int,Boolean]
{
	def apply(x:Int) = f(x)
}

Objects Everywhere

到目前,我们

Scala Doc

www.scala-lang.org/api/current