代码优于描述

一、类型和内置类型详解


1、String

(1)特点

  • 本质上为java的String即java.lang.String
  • 可以使用java String类的一切方法
  • 可以使用scala扩展的函数

(2)scala特有拓展

/*==相当于equals*/
	val s1 ="123"                             //> s1  : String = 123
	val s2 ="123"                             //> s2  : String = 123
	s1==s2                                    //> res0: Boolean = true

/*创建多行字符串*/
	val multiLineStr = """this is
	a multiline
	String
	"""                                       //> multiLineStr  : String = "this is
                                                  //| 	a multiline
                                                  //| 	String
                                                  //| 	"
	//对齐美化
	val str = """123
							|456"""
                                                  //> str  : String = 123
                                                  //| 							|456


/*字符串分割,并去除前后空格*/
	"192.168. 1. 1".split("\\.").map(_.trim); //> res0: Array[String] = Array(192, 168, 1, 1)

/*字符串插值*/
	//s"字符串"
	val s1:String="s1"                        //> s1  : String = s1
	val s2:String = s"this is $s1"            //> s2  : String = this is s1

	val n = 1;                                //> n  : Int = 1
	val s3:String = s"this is 1: ${n==1}"     //> s3  : String = this is 1: true

	//f"字符串", 格式化字符串
	val weight = 100.0;                       //> weight  : Double = 100.0
	println(f"weight:$weight%.2f");           //> weight:100.00

	//row"str" 将字符串中换行输出为\n
	raw"first\nsecond"                        //> res0: String = first\nsecond
	"first\nsecond"                           //> res1: String("first\nsecond") = first
                                                  //| second

/*处理字符串每个字符*/
	"hello,world".filter(_!='l').map(_.toUpper)
                                                  //> res0: String = HEO,WORD
	for(c<-"hello") println(c)                //> h
                                                  //| e
                                                  //| l
                                                  //| l
                                                  //| o
	for(c<-"hello") yield c.toUpper           //> res1: String = HELLO

/*字符串查找模式*/
	val numPattern = "[0-9]+".r  //numPattern: scala.util.matching.Regex = [0-9]+
	val opt = numPattern.findFirstIn("123 afdasf"); //opt: Option[String] = Some(123)
	opt.getOrElse("no match"); //res0: String = 123

	val its = numPattern.findAllIn("123 1231"); //val its = numPattern.findAllIn("123 1231");
its: scala.util.matching.Regex.MatchIterator = non-empty iterator
	its.foreach(println) //123
                        //1231

/*字符串替换模式*/
	val numPattern = "[0-9]+".r               //> numPattern  : scala.util.matching.Regex = [0-9]+
	val opt = numPattern.replaceAllIn("123 afd2324asf","x");
                                                  //> opt  : String = x afdxasf
	val opt1 = numPattern.replaceFirstIn("123 afd234asf","x");
                                                  //> opt1  : String = x afd234asf


/*抽取字符串模式匹配结果*/
	val pattern = "([0-9]+) ([A-Za-z]+)".r    //> pattern  : scala.util.matching.Regex = ([0-9]+) ([A-Za-z]+)
	val pattern(num, str) = "100 nihao"       //> num  : String = 100
                                                  //| str  : String = nihao

   "99 bottles" match {
     case pattern(num,item) => println("num => "+num +" item => " + item)
     case _ =>
   }                                              //> num => 99 item => bottles


/*访问一个字符相当于charAt*/
	"hello"(3)                                        //> res0: Char = l

/*向现有Stirng类添加自定义方法*/
	//定义一个隐式转换的类
	package com.rectcircle.util
	object StringUtil{
		implicit class StringImprovements(val s: String){
			def increment = s.map(c => (c+1).toChar)
			def plusOne = s.toInt + 1
		}
	}

	package com.rectcircle.test

	import com.rectcircle.util.StringUtil._

	object Main extends App{
		println("abc".increment)
	}

	//或者定义在包对象里
	package com.rectcircle
	package object util {
		implicit class StringImprovements(val s: String){
			def increment = s.map(c => (c+1).toChar)
		}
	}

	//使用时	import com.rectcircle.util._

2、数字类型

/*从字符串转数值*/
	"100".toInt                               //> res0: Int = 100
	"100".toDouble
	"100".toFloat
	"100".toLong
	"100".toShort
	"100".toByte
	"aaa".toInt //异常java.lang.NumberFormatException

	//处理进制,可以写成隐式转换包装
	Integer.parseInt("11", 2)                         //> res0: Int = 3
	//关于异常,scala没有受检异常,使用option/some/none

/*数值类型转化*/
	数值类型.toXxx //不会报错,会截断
	数值类型.isValidXxx

/*重载默认数值类型*/
	val a:Int = 1

/*不支持++ 和-- 操作符*/
	var a = 1
	a+=1

/*浮点数比较*/
	//定义一个隐式转换
	def ~=(x: Double, y:Double, precision:Double) = {
		if((x-y).abs < precision) true
		else false
	}                                         //> ~= : (x: Double, y: Double, precision: Double)Boolean

	println(~=(0.1d,0.1d,0.01d))              //> true

/*大数*/
	val a = BigInt("999999")                  //> a  : scala.math.BigInt = 999999
	val b = BigInt("1")                       //> b  : scala.math.BigInt = 1
	val c = BigDecimal("1.111111")            //> a  : scala.math.BigDecimal = 1.111111
	a+b                                       //> res0: scala.math.BigInt = 1000000
	和普通数值类型一样

/*生成随机数*/
	val r = scala.util.Random                 //> r  : util.Random.type = scala.util.Random$@1b4fb997
	r.nextDouble()                            //> res0: Double = 0.9011483551708958
	r.nextDouble()                            //> res1: Double = 0.5142255521939217

	for(i<- 1 to 10) yield r.nextPrintableChar()
                                                  //> res3: scala.collection.immutable.IndexedSeq[Char] = Vector(I, _, E, ', {, a,
                                                  //|  T, z, v, $)

/*创建一个数值区间、列表、或者数组*/
	//数值区间
	val r1 = 1 to 10                          //> r1  : scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7,
                                                  //|  8, 9, 10)
	val r2 = 1 to 10 by 2                     //> r2  : scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
	val r3 = 1 until 5                        //> r3  : scala.collection.immutable.Range = Range(1, 2, 3, 4)


	//数组
	val a = 1 to 5 toArray                    //> a  : Array[Int] = Array(1, 2, 3, 4, 5)
	//列表
	val a = 1 to 5 toList                     //> a  : List[Int] = List(1, 2, 3, 4, 5)

/*格式化*/

二、流程控制结构


1、for与foreach

(1)遍历集合

	val a = Array("a","b","c")                //> a  : Array[String] = Array(a, b, c)
	for(e <- a) println(e)                    //> a
                                                  //| b
                                                  //| c
	for(e<- a){
		//TODO,muiltline
		val s =e.toUpperCase
		println(s)
	}                                         //> A
                                                  //| B
                                                  //| C

	  val m = Map("job" -> "123",
    "tim" -> "321")                               //> m  : scala.collection.immutable.Map[String,String] = Map(job -> 123, tim -> 3
                                                  //| 21)

  for ((k, v) <- m) println(s"key:$k value:$v")   //> key:job value:123
                                                  //| key:tim value:321

(2)从集合中返回值,for表达式

	val a = Array("a","b","c")                //> a  : Array[String] = Array(a, b, c)
	for(e <- a) yield e.toUpperCase()         //> res0: Array[String] = Array(A, B, C)

(3)循环计数器

	val a = Array("a","b","c")                //> a  : Array[String] = Array(a, b, c)
	for(i <- 0 until a.length) {
		println(s"this is ${a(i)}")       //> this is a
                                                  //| this is b
                                                  //| this is c
	}
	//多个计数器
	for(i<- 1 to 2 ; j<-1 to 2 ){
		println(s"[$i,$j]")               //> [1,1]
                                                  //| [1,2]
                                                  //| [2,1]
                                                  //| [2,2]
	}

(4)生成器和卫语句

	for(i <- 1 to 3 if i!= 2) println(i)      //> 1
                                                  //| 3

	for(i <- 1 to 3 if(i!=2&&i!=3)) println(i)//> 1

(5)实现break和continue

import scala.util.control.Breaks._

//实现break
breakable {
	for(i<- 0 to 10){
		if(i==3) break
		println(i)
	}
}                                         //> 0
																								//| 1
																								//| 2
//实现continue
for(i<- 1 to 3){
	breakable {
		if(i==2) break
		println(i)
	}                                 //> 1
																								//| 3
}

//有标签的break

2、没有就三元运算符,用if else代替

val i = 0                                 //> i  : Int = 0
if(i==0)"a" else "b"                      //> res0: String = a

3、匹配表达式

(1)基础用法像switch语句使用

val i = 1                                 //> i  : Int = 1
i match{
	case 1 => "January"
	case 2 => "February"
	case _ => "Invalid"
}                                         //> res0: String = January

//类型匹配
val i:Any = 1                             //> i  : Any = 1
i match{
	case s:String => "String"
	case i:Int => "Int"
	case _ => "Invalid"
}                                         //> res0: String = Int

(2)一条case匹配多个条件

val i = 1                                 //> i  : Int = 1
	i match{
		case 0|2|4 => "even"
		case 1|3|5 => "odd"
		case _ => "Invalid"
	}                                         //> res0: String = odd

(3)访问匹配表达式的缺省case值

val i = 3                                 //> i  : Int = 3
i match{
	case 0 => "even"
	case 1 => "odd"
	case default => default
}                                         //> res0: Any = 3

(4)使用模式匹配

支持

  • 普通值匹配
  • 序列匹配
  • 元组匹配
  • 构造器匹配
  • 类型匹配
  • 默认匹配

    def echoWhatYouGaveMe(x: Any): String = x match {
    
    // constant patterns
    case 0 => "zero"
    case true => "true"
    case "hello" => "you said 'hello'"
    case Nil => "an empty List"
    
    // sequence patterns
    case List(0, _, _) => "a three-element list with 0 as the first element"
    case List(1, _*) => "a list beginning with 1, having any number of elements"
    case Vector(1, _*) => "a vector starting with 1, having any number of elements"
    
    // tuples
    case (a, b) => s"got $a and $b"
    case (a, b, c) => s"got $a, $b, and $c"
    
    // constructor patterns
    case Person(first, "Alexander") => s"found an Alexander, first name = $first"
    case Dog("Suka") => "found a dog named Suka"
    
    // typed patterns
    case s: String => s"you gave me this string: $s"
    case i: Int => s"thanks for the int: $i"
    case f: Float => s"thanks for the float: $f"
    case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
    case as: Array[String] => s"an array of strings: ${as.mkString(",")}"
    case d: Dog => s"dog: ${d.name}"
    case list: List[_] => s"thanks for the List: $list"
    case m: Map[_, _] => m.toString
    
    // the default wildcard pattern
    case _ => "Unknown"
    }

(4)匹配case类

	trait Animal
	case class Dog(name: String) extends Animal
	case class Cat(name: String) extends Animal

	case object Woodpecker extends Animal

	def determineType(x: Animal): String = x match {
        case Dog(moniker) => "Got a Dog, name = " + moniker
        case _:Cat => "Got a Cat (ignoring the name)"
        case Woodpecker => "That was a Woodpecker"
        case _ => "That was something else"
    }                                             //> determineType: (x: worksheet.Animal)String
    println(determineType(new Dog("Rocky")))      //> Got a Dog, name = Rocky
    println(determineType(new Cat("Rusty the Cat")))
                                                  //> Got a Cat (ignoring the name)
    println(determineType(Woodpecker))            //> That was a Woodpecker

(5)给case语句添加if表达式

val i = 2                                 //> i  : Int = 2
i match {
	case x if 0 to 9 contains x => x
}                                         //> res0: Int = 2

(6)使用此替代isInstanceOf方法

4

(7)使用List

	//将list拼接成字符串
	val y = 1::2::3::Nil                      //> y  : List[Int] = List(1, 2, 3)

	def listToString(list: List[_]):String = list match {
		case s::rest =>s+" "+listToString(rest)
		case Nil => ""
	}                                         //> listToString: (list: List[_])String
	listToString(y)                           //> res0: String = "1 2 3 "

4、使用try/catch匹配异常

(1)匹配异常

	val s="abc"                               //> s  : String = abc
	try{
		s.toInt
	} catch {
		case e : Exception => "catch a exception"
	}                                         //> res0: Any = catch a exception

(2)在try外部定义变量

5、while操作符

var i = 0;                                //> i  : Int = 0
while(i<5){
	println(i)
	i+=1;
}                                         //> 0
																								//| 1
																								//| 2
																								//| 3

5、创建自定义控制结构

调用

import com.rectcircle.util.dsl._

object Greeting extends App {
  var i = 0
  whilst(i<5){
    println(i)
    i += 1
  }

}

实现

package com.rectcircle.util
import scala.annotation.tailrec
case object dsl {
  //  def whilst(testCondition: => Boolean)(codeBlock: => Unit) {
  //    while (testCondition) {
  //      codeBlock
  //    }
  //  }

  @tailrec
  def whilst(testCondition: => Boolean)(codeBlock: => Unit) {
    if (testCondition) {
      codeBlock
      whilst(testCondition)(codeBlock)
    }
  }

}

三、类和对象


1、类和属性

(1)创建一个主构造函数

类似于js构造函数

 class Person(var firstName: String, var lastName: String) {
    println("the constructor begins")
    // 相当于java private  final
    private val HOME = System.getProperty("user.home")
    var age = 0
    // some methods
    override def toString = s"$firstName $lastName is $age years old"
    def printHome { println(s"HOME = $HOME") }
    def printFullName { println(this) } // uses toString
    printHome
    printFullName
    println("still in the constructor")
  }

	val p = new Person("adam","meyer")        //> the constructor begins
                                                  //| HOME = C:\Users\sunben960729
                                                  //| adam meyer is 0 years old
                                                  //| still in the constructor
                                                  //| p  : worksheet.Person = adam meyer is 0 years old

	p.firstName = "bb" //firstname是val可重新负值

java版Person

public class Person {
	private String firstName;
	private String lastName;
	private final String HOME = System.getProperty("user.home");
	private int age;

	public Person(String firstName, String lastName) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		System.out.println("the constructor begins");
		age = 0;
		printHome();
		printFullName();
		System.out.println("still in the constructor");
	}

	public String firstName() {
		return firstName;
	}

	public String lastName() {
		return lastName;
	}

	public int age() {
		return age;
	}

	public void firstName_$eq(String firstName) {
		this.firstName = firstName;
	}

	public void lastName_$eq(String lastName) {
		this.lastName = lastName;
	}

	public void age_$eq(int age) {
		this.age = age;
	}

	public String toString() {
		return firstName + " " + lastName + " is " + age + " years old";
	}

	public void printHome() {
		System.out.println(HOME);
	}

	public void printFullName() {
		System.out.println(this);
	}
}

说明

  • _eq方法,var 定义变量的语法糖,自动添加set方法

(2)控制构造函数参数的可见性

  • 被声明为var(包括字段:主构造函数定义的变量),会添加get/set方法、
  • 是val,只生成get方法
  • 没有val和var不会生成get/set
  • val var被private修饰不会生成get/set
  • 以上的get/set方法不会遵循javabean命名规范
  • case类没有val和var,任然会编译成val
var声明字段
	class Person(var name:String)
	val p = new Person("nihao")               //> p  : worksheet.Person = worksheet$Person@5e8c92f4
	p.name                                    //> res0: String = nihao
	p.name="hehe"
	p.name                                    //> res1: String = hehe
非var和非val字段
	class Person(name:String)
	val p = new Person("nihao")
	p.name //报错
	p.name="hehe" //报错
	p.name //报错
添加private修饰
	class Person(private var name:String)
	val p = new Person("nihao")
	p.name //报错
	p.name="hehe" //报错
	p.name //报错

(3)辅助构造函数

  • 参数列表必须不完全相同
  • 必须调用一个已定义的构造函数

    class Pizza(var crustSize: Int, var crustType: String) {
    // one-arg auxiliary constructor
    def this(crustSize: Int) {
      this(crustSize, Pizza.DEFAULT_CRUST_TYPE)
    }
    		//……
    }
为case类生成构造函数
  case class Person(var name: String, var age: Int)
  // the companion object
  object Person {
    def apply() = new Person("<no name>", 0)
    def apply(name: String) = new Person(name, 0)
  }
  val a = Person() // corresponds to apply()      //> a  : worksheet.Person = Person(<no name>,0)
  val b = Person("Pam") // corresponds to apply(name: String)
                                                  //> b  : worksheet.Person = Person(Pam,0)
  val c = Person("William Shatner", 82)           //> c  : worksheet.Person = Person(William Shatner,82)
  println(a)                                      //> Person(<no name>,0)
  println(b)                                      //> Person(Pam,0)
  println(c)                                      //> Person(William Shatner,82)
  // verify the setter methods work
  a.name = "Leonard Nimoy"
  a.age = 82
  println(a)                                      //> Person(Leonard Nimoy,82)

(4)定义私有构造函数,单例

class Person private(name:String)
val p = new Person("fasd") //报错

//实现単例
class Person private(name:String)
object Person {
	val person = new Person("nihao")
	def getInstance = person
}

val p = Person.getInstance                      //> p  : worksheet.Person = worksheet$Person@5e8c92f4

伴生对象,在类的同一文件中与类有相同的名字,定义关键字为object。 伴生对象的任意方法将变成实例对象的静态方法 apply方法

//使用伴生对象创建工具类

object FileUtils {
	def readFile(filename: String) = {
		// code here ...
	}
	def writeToFile(filename: String, contents: String) {
		// code here ...
	}
}

(5)构造函数参数的默认值

class Socket (val timeout: Int = 10000)

(6)覆盖默认getset方法

  • name_=是scala语法糖

    class Person(private var _name: String) {
    	def name = _name // accessor
    	def name_=(aName: String) { _name = aName } // mutator
    }
    
    val p = new Person("niahoa")              //> p  : worksheet.Person = worksheet$Person@5e8c92f4
    p.name                                    //> res0: String = niahoa
    p.name = "fasdf"

符合javabean的get、set

class Person(private var _name: String) {
	def name = _name // accessor
	def name_=(aName: String) { _name = aName } // mutator
	def getName = _name
	def setName(name:String) = _name = name
}

val p = new Person("niahoa")              //> p  : worksheet.Person = worksheet$Person@5e8c92f4
p.name                                    //> res0: String = niahoa
p.name = "fasdf"

p.getName                                 //> res1: String = fasdf
p.setName("ttwr")
p.getName                                 //> res2: String = ttwr

(7)阻止字段生成get/set

普通情况

class Person {
	var name:String =_
}

val p = new Person                        //> p  : worksheet.Person = worksheet$Person@5e8c92f4

p.name                                    //> res0: String = null
p.name = "123"

不生成get/set

class Person {
	private var name:String =_
}

val p = new Person

p.name //报错
p.name = "123" //报错

(8)将代码块或函数赋给字段

class Foo {
	// set 'text' equal to the result of the block of code
	//可以考虑lazy val
	val text = {
		var lines = ""
		try {
			lines = io.Source.fromFile("/etc/passwd").getLines.mkString
		} catch {
			case e: Exception => lines = "Error happened"
		}
		lines
	}
	println(text)
}

val f = new Foo                           //> Error happened
																								//| f  : worksheet.Foo = worksheet$Foo@5b464ce8

(9)设置为初始化var类型

不要使用null,使用Option

var i = 0 // Int
var d = 0.0 // Double
var b: Byte = 0
var c: Char = 0
var f: Float = 0
var l: Long = 0
var s: Short = 0
var age = 0
var firstName = ""
var lastName = ""
var address = None: Option[Address]

(10)在继承类时处理构造函数参数

  • 在子类构造函数中,不要使用var或val定义要传给父类的参数

    case class Address (city: String, state: String)
    
    class Person(var name: String, var address: Address) {
    	override def toString = if (address == null) name else s"$name @ $address"
    }
    
    class Employee(name: String, address: Address, var age: Int)
    	extends Person(name, address) {
    	// rest of the class
    }
    
    val teresa = new Employee("Teresa", Address("Louisville", "KY"), 25)
    																								//> teresa  : worksheet.Employee = Teresa @ Address(Louisville,KY)

(11)调用父类的构造函数

子类可以调用父类的任意一个构造函数
class Animal(var name: String, var age: Int) {
	def this(name: String) {
		this(name, 0)
	}
	override def toString = s"$name is $age years old"
}

class Dog(name: String) extends Animal(name) {
	println("Dog constructor called")
}

class Dog(name: String) extends Animal(name, 0) {
	println("Dog constructor called")
}
子类的辅助构造函数

应为scala必须存在构造函数链,最后一定会执行主构造函数,所以辅助构造函数不用显示的调用父类构造函数

case class Address(city: String, state: String)
case class Role(role: String)

class Person(var name: String, var address: Address) {
	def this(name: String) {
		this(name, null)
		address = null
	}
	override def toString = if (address == null) name else s"$name @ $address"
}

class Employee(name: String, role: Role, address: Address)
	extends Person(name, address) {
	def this(name: String) {
		this(name, null, null)
	}
	def this(name: String, role: Role) {
		this(name, role, null)
	}
	def this(name: String, address: Address) {
		this(name, null, address)
	}
}

(12)何时使用抽象类

  • 需要创建一个有构造函数参数的基类
  • 需要被java调用

scala中的特质(trait)

  • 不允许有构造函数参数
  • java无法调用带方法实现的scala trait
  • 使用abstract修饰类
abstract class BaseController(db: Database) {
	def save { db.save }
	def update { db.update }
	def delete { db.delete }
	// abstract
	def connect
	// an abstract method that returns a String
	def getStatus: String
	// an abstract method that takes a parameter
	def setServerName(serverName: String)
}

(13)在抽象类或者特质中定义属性

  • 抽象类中的var属性会编译成scala的get/set方法
  • 抽象类中的val属性会编译成scala的get方法
  • 但是不会创建这么一个字段
  • 子类需要重新定义属性

    abstract class Pet(name: String) {
    	val greeting: String
    	var age: Int
    	def sayHello { println(greeting) }
    	override def toString = s"I say $greeting, and I'm $age"
    }
    class Dog(name: String) extends Pet(name) {
    	val greeting = "Woof"
    	var age = 2
    }
    class Cat(name: String) extends Pet(name) {
    	val greeting = "Meow"
    	var age = 5
    }
    
    val d = new Dog("dog")                    //> d  : worksheet.Dog = I say Woof, and I'm 2

(14)使用case类生成模板代码

case class主要是为了创建不可变记录 定义一个case class scala会自动生成模板代码包括:

  • 生成apply方法,不用new
  • val(或者不写)成get, var生成get/set
  • 生成默认toString方法
  • 生成unapply,模式匹配好用
  • equals()hashCode()
  • 一个copy方法
case class Person(name: String, relation: String)
val p = Person("name", "fsadfd")          //> p  : worksheet.Person = Person(name,fsadfd)
p.name                                    //> res0: String = name
//p.name = "fsadfd" //报错

(15)定义一个equals方法

scala 对象的 == 会调用equals方法

val p1 = new Person("111",1)              //> p1  : worksheet.Person = worksheet$Person@c1f1
val p2 = new Person("111",1)              //> p2  : worksheet.Person = worksheet$Person@c1f1
p1 == p2                                  //> res0: Boolean = true

(16)创建内部类

class PandorasBox {
	case class Thing(name: String)
	var things = new collection.mutable.ArrayBuffer[Thing]()
	things += Thing("Evil Thing #1")
	things += Thing("Evil Thing #2")
	def addThing(name: String) { things += new Thing(name) }
}

eg.2

class OuterClass {
	class InnerClass {
		var x = 1
	}
}
val oc1 = new OuterClass                  //> oc1  : worksheet.OuterClass = worksheet$OuterClass@5e8c92f4
val oc2 = new OuterClass                  //> oc2  : worksheet.OuterClass = worksheet$OuterClass@6a5fc7f7
val ic1 = new oc1.InnerClass              //> ic1  : worksheet.oc1.InnerClass = worksheet$OuterClass$InnerClass@3b6eb2ec
val ic2 = new oc2.InnerClass              //> ic2  : worksheet.oc2.InnerClass = worksheet$OuterClass$InnerClass@1e643faf
ic1.x = 10
ic2.x = 20
println(s"ic1.x = ${ic1.x}")              //> ic1.x = 10
println(s"ic2.x = ${ic2.x}")              //> ic2.x = 20

2、方法

(1)控制方法作用域

默认为public 从严格到宽松

  • Object-private scope对象私有作用域
    • private[this] def isFoo = true
  • Private
    • private def ...
  • Package
    • protected def
  • Package-specific
    • private[model] def doX {}
  • Public
    • def doX {}
对象私有作用域
class Foo {
	private[this] def isFoo = true

	def doSth(){
		if(isFoo){ //正确

		}
	}

	def doFoo(other: Foo) {
		if (other.isFoo) { // 报错
			// ...
		}
	}
}
私有作用域
class Foo {
	private def isFoo = true
	def doFoo(other: Foo) {
		if (other.isFoo) { // 正确
			// ...
		}
	}
}
//子类不可访问
class Animal {
	private def heartBeat {}
}
class Dog extends Animal {
	heartBeat // 报错
}
保护作用域——子类可见
class Animal {
	protected def breathe {}
}
class Dog extends Animal {
	breathe
}
包作用域
package com.acme.coolapp.model {
	class Foo {
		private[model] def doX {}
		private def doY {}
	}
	class Bar {
		val f = new Foo
		f.doX // 可编译
		f.doY // 不能编译
	}
}
开放作用域
package com.acme.coolapp.model {
	class Foo {
		def doX {}
	}
}
package org.xyz.bar {
	class Bar {
		val f = new com.acme.coolapp.model.Foo
		f.doX
	}
}

(2)调用父类方法

  • super.xXxx
  • super[父类].xXxx

一般形式

class FourLeggedAnimal {
	def walk { println("I'm walking") }
	def run { println("I'm running") }
}
class Dog extends FourLeggedAnimal {
	def walkThenRun {
		super.walk
		super.run
	}
}

调用特质方法(用with继承多个父类)

trait Human {
	def hello = "the Human trait"
}
trait Mother extends Human {
	override def hello = "Mother"
}
trait Father extends Human {
	override def hello = "Father"
}
class Child extends Human with Mother with Father {
	def printSuper = super.hello
	def printMother = super[Mother].hello
	def printFather = super[Father].hello
	def printHuman = super[Human].hello
}
val c = new Child                         //> c  : worksheet.Child = worksheet$Child@5e8c92f4
println(s"c.printSuper = ${c.printSuper}")//> c.printSuper = Father
println(s"c.printMother = ${c.printMother}")
																								//> c.printMother = Mother
println(s"c.printFather = ${c.printFather}")
																								//> c.printFather = Father
println(s"c.printHuman = ${c.printHuman}")//> c.printHuman = the Human trait

若间接继承特质,不可调用特质方法

trait Animal {
	def walk { println("Animal is walking") }
}
class FourLeggedAnimal extends Animal {
	override def walk { println("I'm walking on all fours") }
}
class Dog extends FourLeggedAnimal {
	def walkThenRun {
		super.walk
		super[FourLeggedAnimal].walk
		super[Animal].walk // error
	}
}

(3)方法参数的默认值

class Connection {
	def makeConnection(timeout: Int = 5000, protocol: String = "http") {
		println("timeout = %d, protocol = %s".format(timeout, protocol))
		// more code here
	}
}
val c = new Connection
c.makeConnection //报错
c.makeConnection()
c.makeConnection(2000)
c.makeConnection(3000, "https")

//可以带上变量名
c.makeConnection(timeout=10000)
c.makeConnection(protocol="https")
c.makeConnection(protocol="https", timeout=10000)

(4)使用参数名传参,参数顺序任意

methodName(param1=value1, param2=value2, ...)

(5)定义一个返回多个值的tuples(元组)方法,代替java临时包装类

def getStockInfo = ("NFLX", 100.00)               //> getStockInfo: => (String, Double)
val (symbol, currentPrice) = getStockInfo         //> symbol  : String = NFLX
                                                  //| currentPrice  : Double = 100.0
symbol                                            //> res0: String = NFLX
currentPrice                                      //> res1: Double = 100.0

(6)调用get/set方法不要使用括号

任意无副作用getter的声明不应该加括号

class Pizza {
	def crustSize = 12
}
val p = new Pizza

p.crustSize  //正确
p.crustSize() //报错

函数的副作用包括

  • IO输出
  • IO读
  • 又该一个参数的状态,或修改一个对象的某个属性值
  • 抛出异常,出错导致程序停止
  • 调用其他具有副作用的函数

(7)创建接受可变参数的方法

  • 一个方法只允许拥有一个可变参数
  • 可变参数必须在参数列表的最后一个
  • def printAll(strings: String*) {
  • 可变参数实际上是一个数组

    def printAll(strings: String*) {
    	strings.foreach(println)
    }
    def printAll(strings: String*, i: Int) { //报错
    	strings.foreach(println)
    }
    
    //传入数组
    val arr= Array("What's","up","doc?")
    printAll(arr:_*)

(8)方法异常声明

@throws(classOf[XxxException])

  • scala 不要求处理异常

    @throws(classOf[Exception])
    override def play {
    // exception throwing code here ...
    }
    
    @throws(classOf[IOException])
    @throws(classOf[LineUnavailableException])
    @throws(classOf[UnsupportedAudioFileException])
    def playSoundFileWithJavaAudio {
    // exception throwing code here ...
    }

(9)方法链式调用的支持

使用条件

  • 如果类可能会被继承,那么使用this.type作为返回值
  • 如果确定里不会被扩展,那么直接返回this,不用显示的指定返回类型为this.type
会被继承
class Person {
	protected var fname = ""
	protected var lname = ""
	def setFirstName(firstName: String): this.type = {
		fname = firstName
		this
	}
	def setLastName(lastName: String): this.type = {
		lname = lastName
		this
	}
}
class Employee extends Person {
	protected var role = ""
	def setRole(role: String): this.type = {
		this.role = role
		this
	}
	override def toString = {
		"%s, %s, %s".format(fname, lname, role)
	}
}
 val employee = new Employee              //> employee  : worksheet.Employee = , ,

 employee.setFirstName("Al")
 .setLastName("Alexander")
 .setRole("Developer")                    //> res0: worksheet.employee.type = Al, Alexander, Developer

 println(employee)                        //> Al, Alexander, Developer
不会被继承
final class Pizza {
	import scala.collection.mutable.ArrayBuffer
	private val toppings = ArrayBuffer[String]()
	private var crustSize = 0
	private var crustType = ""
	def addTopping(topping: String) = {
		toppings += topping
		this
	}
	def setCrustSize(crustSize: Int) = {
		this.crustSize = crustSize
		this
	}
	def setCrustType(crustType: String) = {
		this.crustType = crustType
		this
	}
	def print() {
		println(s"crust size: $crustSize")
		println(s"crust type: $crustType")
		println(s"toppings: $toppings")
	}
}
val p = new Pizza                         //> p  : worksheet.Pizza = worksheet$Pizza@26f0a63f
p.setCrustSize(14)
	.setCrustType("thin")
	.addTopping("cheese")
	.addTopping("green olives")
	.print()                          //> crust size: 14
																								//| crust type: thin
																								//| toppings: ArrayBuffer(cheese, green olives)

(10)函数柯里化

//多参数列表方式
//定义
def curriedSum(x:Int)(y:Int) = x + y
//调用
curriedSum(1)(1)
//柯里化
val onePlus = curriedSum(1)_

等价于单参数列表形式
//定义
def sum(x:Int) = (y:Int) => x + y //返回一个函数类型
//调用
sum(1)(1)
//柯里化
def plusOne = sum(1)

(11)传名参数(创建新的控制结构)

f:=>Boolean

单元测试框架

//定义
def test(desc:String)(f:=>Boolean) =
	if(f) println(s"${desc}:成功")
	else println(s"${desc}:失败")

//调用
test("test1"){
	val a=1
	val b=1
	2 == a+b
}

3、对象

对象的含义

  • java的类实例
  • scala的关键字
    • object
    • package object
    • 伴生object

(1)实例对象强制转换

object.asInstanceOf[类型]

(2)获取类的class对象(java.class)

 classOf[Xxx] //等价于java Xxx.class

(3)确定实例对象所属的类

val a = "asd"                             //> a  : String = asd
a.getClass                                //> res0: Class[?0] = class java.lang.String

(4)用object启动一个应用

方法一、实现一个继承App特质的object

object Hello extends App{
	if(args.length==1)
		println("${args(0)}")
	else
		println("Hello")
}

方法二、实现创建一个拥有main方法的object

object Hello {
	def main(args: Array[String]){
		println("Hello")
	}
}

(5)用object创建单例

object可以理解为静态类,所有方法,通过objectName调用

(6)用伴生类创建静态成员

  • 在同一文件中创建class和object且具有相同的名字
  • 在object中定义静态成员
  • 在class中定义实例方法
  • object和class可以互相问私有成员

一般用法

class Pizza(var crustType: String) {
	override def toString = "Crust type is " + crustType
}

object Pizza {
	val CRUST_TYPE_THIN = "thin"
	val CRUST_TYPE_THICK = "thick"
	def getFoo = "Foo"
}

互相访问私有成员

class Foo {
	private val secret = 2
}
object Foo {
	def double(foo: Foo) = foo.secret * 2
}
object Driver extends App {
	val f = new Foo
	println(Foo.double(f)) // prints 4
}

(7)把通用代码放在包对象package object中

在不引入类和对象的情况下,让函数字段在其他代码在包级别可用

package com.alvinalexander.myapp
package object model {
	// 字段
	val MAGIC_NUM = 42
	// 方法
	def echo(a: Any) { println(a) }
	// 枚举
	object Margin extends Enumeration {
		type Margin = Value
		val TOP, BOTTOM, LEFT, RIGHT = Value
	}
	// 类型
	type MutableMap[K, V] = scala.collection.mutable.Map[K, V]
	val MutableMap = scala.collection.mutable.Map
}

上例子:

  • 放置在 com/alvinalexander/myapp/model文件夹内
  • 文件名为package.scala

(8)不使用new创建对象

  • 为类创建伴生对象,并定义一个apply方法
  • 将类定义为case类

apply方法

class Person {
	var name: String = _
}
object Person {
	def apply(name: String): Person = {
		var p = new Person
		p.name = name
		p
	}
}
val dawn = Person("Dawn")                 //> dawn  : worksheet.Person = worksheet$Person@5e8c92f4
val a = Array(Person("Dan"), Person("Elijah"))
																								//> a  : Array[worksheet.Person] = Array(worksheet$Person@2d6e8792, worksheet$Pe
																								//| rson@2812cbfa)

将类定义为case类

case class Person (var name: String)

(9)用apply实现工厂方法

trait Animal {
	def speak
}
object Animal {
	private class Dog extends Animal {
		override def speak { println("woof") }
	}
	private class Cat extends Animal {
		override def speak { println("meow") }
	}
	// the factory method
	def apply(s: String): Animal = {
		if (s == "dog") new Dog
		else new Cat
	}
}
val cat = Animal("cat") // returns a Cat
val dog = Animal("dog") // returns a Dog

四、包和引入


1、特点

  • 随处可以使用import语句
  • 导入类、包或对象
  • 导入类是隐藏或者重命名
  • 一个文件可以创建多个包类

2、创建

c#风格包标记法

package com.acme.store {
 class Foo { override def toString = "I am com.acme.store.Foo" }
}

java风格包定义

package com.acme.store
class Foo { override def toString = "I am com.acme.store.Foo" }

2、导入

引入单个类

import java.io.File

引入包内多个类

import java.io._
import java.io.{File, IOException, FileNotFoundException}

在块作用域内有效的import语句

package foo
import java.io.File
import java.io.PrintWriter
class Foo {
	import javax.swing.JFrame // only visible in this class
	// ...
}

导入时重命名类

import java.util.{ArrayList => JavaList}
import java.util.{Date => JDate, HashMap => JHashMap}

导入时隐藏类

import java.util.{Random => _, _}
import java.util.{List => _, Map => _, Set => _, _}

五、特质


1、特质用作接口

trait BaseSoundPlayer {
	def play
	def close
	def pause
	def stop
	def resume
}

trait Dog {
	def speak(whatToSay: String):Unit
	def wagTail(enabled: Boolean)
}

class Mp3SoundPlayer extends BaseSoundPlayer { ...
class Foo extends BaseClass with Trait1 with Trait2 { ...
class Foo extends Trait1 with Trait2 with Trait3 with Trait4 { ...

2、特质中定义抽象和实际字段

trait PizzaTrait {
	var numToppings: Int // abstract
	var size = 14 // concrete
	val maxNumToppings = 10 // concrete
}

class Pizza extends PizzaTrait {
	var numToppings = 0 // 'override' not needed
	size = 16 // 'var' and 'override' not needed
	override val maxNumToppings = 10 // 'override' 必须
}

3、像抽象类一样使用特质、

trait Pet {
	def speak { println("Yo") } // concrete implementation
	def comeToMaster // abstract method
}
class Dog extends Pet {
	// don't need to implement 'speak' if you don't need to
	def comeToMaster { ("I'm coming!") }
}
class Cat extends Pet {
	// override the speak method
	override def speak { ("meow") }
	def comeToMaster { ("That's not gonna happen.") }
}

4、简单混入 特质

使用with

5、通过继承限制特质的使用范围

trait [TraitName] extends [SuperThing] 该特质只能被继承了SuperThing的类使用

class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class Starship extends StarfleetComponent with StarfleetWarpCore

class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class RomulanStuff
// won't compile
class Warbird extends RomulanStuff with StarfleetWarpCore

6、限定特质只可用于指定类型的子类

//基本
trait MyTrait {
	this: BaseType =>
}
//特殊
trait WarpCore {
	this: Starship with WarpCoreEjector with FireExtinguisher =>
}

class Starship
trait WarpCoreEjector
trait FireExtinguisher

// this works
class Enterprise extends Starship
	with WarpCore
	with WarpCoreEjector
	with FireExtinguisher

7、保证特质只能添加到只有一个特定方法的类型

trait WarpCore {
	this: { def ejectWarpCore(password: String): Boolean } =>
}
class Starship {
	// code here ...
}
class Enterprise extends Starship with WarpCore {
	def ejectWarpCore(password: String): Boolean = {
		if (password == "password") {
			println("ejecting core")
			true
		} else {
			false
		}
	}
}

8、为object类型添加特质

object Test extends App {
	val hulk = new DavidBanner with Angry
}

9、像特质一样继承一个Java接口

public interface Animal {
	public void speak();
}
public interface Wagging {
	public void wag();
}
public interface Running {
	public void run();
}
class Dog extends Animal with Wagging with Running {
	def speak { println("Woof") }
	def wag { println("Tail is wagging!") }
	def run { println("I'm running!") }
}

六、函数式编程


1、使用匿名函数(函数字面量)

匿名函数

(i: Int) => i % 2 == 0

匿名函数作为参数,简化过程

val x = List.range(1, 3)                          //> x  : List[Int] = List(1, 2)
x.foreach((i:Int) => println(i))                  //> 1
                                                  //| 2
x.foreach((i) => println(i))                      //> 1
                                                  //| 2
x.foreach(i => println(i))                        //> 1
                                                  //| 2
x.foreach(println(_))                             //> 1
                                                  //| 2
x.foreach(println)                                //> 1
                                                  //| 2

2、将函数赋给变量

(1)将匿名函数赋给变量

//完整形式
//val|var 变量名:函数签名 = (参数列表) => 函数体
//其中函数签名为 参数类型 => 返回值类型
val p:(Int) => Unit = (x) => {println(x)}         //> p  : Int => Unit = <function1>
//一个参数,函数体可以去掉括号
val p1:Int =>Unit = x => println(x)               //> p1  : Int => Unit = <function1>
//省略函数签名
val p2 = (x:Int) => {println(x)} //常用             //> p2  : Int => Unit = <function1>
//省略括号
val p3 = (x:Int) => println(x)                    //> p3  : Int => Unit = <function1>

(2)将已存在的函数赋给变量

val c = scala.math.cos _                          //> c  : Double => Double = <function1>
val c1 = scala.math.cos(_)                        //> c1  : Double => Double = <function1>

val p = scala.math.pow(_, _)                      //> p  : (Double, Double) => Double = <function2>

val sqrt = scala.math.pow(_:Double, 0.5)          //> sqrt  : Double => Double = <function1>
val sqrt = (x:Double) => scala.math.pow(x, 0.5)   //> sqrt  : Double => Double = <function1>

(3)定义接受回调函数的方法

def executeFunction(callback: () => Unit) {
	callback()
}                                         //> executeFunction: (callback: () => Unit)Unit

(4)闭包

注意代码中的$hello

def exec(f: (String) => Unit, name: String) {
	f(name)
}                                         //> exec: (f: String => Unit, name: String)Unit

var hello = "Hello"                       //> hello  : String = Hello
def sayHello(name: String) { println(s"$hello, $name") }
																								//> sayHello: (name: String)Unit

exec(sayHello, "Al")                      //> Hello, Al
hello = "Hola"
exec(sayHello, "Lorenzo")                 //> Hola, Lorenzo

(5)部分应用函数

val sqrt = scala.math.pow(_:Double, 0.5)          //> sqrt  : Double => Double = <function1>

(6)返回函数

def saySomething(prefix: String) = (s: String) => {
	prefix + " " + s
}                                         //> saySomething: (prefix: String)String => String

val sayHello = saySomething("Hello")      //> sayHello  : String => String = <function1>

(7)创建偏函数

普通实现

val divide = new PartialFunction[Int, Int] {
	def apply(x: Int) = 42 / x
	def isDefinedAt(x: Int) = x != 0
}                                         //> divide  : PartialFunction[Int,Int] = <function1>

divide.isDefinedAt(0)                     //> res0: Boolean = false
divide(2)                                 //> res1: Int = 21

case实现

val divide2: PartialFunction[Int, Int] = {
	case d: Int if d != 0 => 42 / d
}                                                 //> divide2  : PartialFunction[Int,Int] = <function1>

例三

// converts 1 to "one", etc., up to 5
val convertLowNumToString = new PartialFunction[Int, String] {
 val nums = Array("one", "two", "three", "four", "five")
 def apply(i: Int) = nums(i-1)
 def isDefinedAt(i: Int) = i > 0 && i < 6
}

使用orElse和andThen

val convert1to5 = new PartialFunction[Int, String] {
	val nums = Array("one", "two", "three", "four", "five")
	def apply(i: Int) = nums(i - 1)
	def isDefinedAt(i: Int) = i > 0 && i < 6
}                                         //> convert1to5  : PartialFunction[Int,String]{val nums: Array[String]} = <funct
																								//| ion1>
val convert6to10 = new PartialFunction[Int, String] {
	val nums = Array("six", "seven", "eight", "nine", "ten")
	def apply(i: Int) = nums(i - 6)
	def isDefinedAt(i: Int) = i > 5 && i < 11
}                                         //> convert6to10  : PartialFunction[Int,String]{val nums: Array[String]} = <func
																								//| tion1>

val handle1to10 = convert1to5 orElse convert6to10
																								//> handle1to10  : PartialFunction[Int,String] = <function1>

handle1to10(3)                            //> res0: String = three
handle1to10(8)                            //> res1: String = eight