Skip to content

Instantly share code, notes, and snippets.

@ericzhong
Created December 1, 2017 04:05
Show Gist options
  • Save ericzhong/228107e3336d5007fcae90d767af7e75 to your computer and use it in GitHub Desktop.
Save ericzhong/228107e3336d5007fcae90d767af7e75 to your computer and use it in GitHub Desktop.
Scala 语言

介绍

Scala = Scalable Language

纯面向对象、函数式编程、静态类型、扩展性(以库的形式无缝对接其它语言)、并发性。

Scala 源代码被编译成 Java 字节码,运行于 JVM 之上,可以调用现有的 Java 类库。

语法

Hello World

// hello.scala
object HelloWorld {      // 对象
  // (参数:类型):返回值类型
  def main(args: Array[String]): Unit = {      // 入口必须是 main
    println("Hello, world!")
  }
}

直接运行:

$ scala hello.scala 
Hello, world!

也可以先编译再运行:

$ scalac hello.scala     # 生成 HelloWorld.class, HelloWorld$.class
$ scala HelloWorld       # 类名
Hello, world!

scala 跟 python 一样,也是个交互式的 Shell。

基础

区分大小写

类名首字母大写,每个单词的首字母大写:class MyFirstScalaClass

方法名首字母小写: def myMethodName

文件名与 object 名称一致

入口:def main(args: Array[String])

注释:///* ... */

语句结尾的 ; 可选

引用

// 可以放在任何地方
import java.awt.Color
import java.awt._                           // 引入包内所有成员
import java.awt.{Color, Font}
import java.util.{HashMap => JavaHashMap}   // 重命名
import java.util.{HashMap => _, _}          // 引入所有成员,但 HashMap 被隐藏

数据类型

Byte,Short,Int,Long
Float,Double
Char,String
Boolean
Unit       // 类似 void,表示无返回值
Null
Nothing    // 它是任何其他类型的子类型
Any        // 是所有其它类的超类
Anyref     // 所有引用类的基类

字符 & 字符串:

'c'            // char
"string"
"""string"""   // 可分行

变量声明

// 名 : 类型
var myVar : String = "Foo"      // 变量。必须给初始值。
val myVal : String = "Foo"      // 常量
var myVar = 10;                 // 自动判断类型
val myVal = "Hello, Scala!";
val xmax, ymax = 100
val pa = (40,"Foo")             // 元组用 val

访问修饰符

public
private      // 仅类或对象的内部可见
protected    // 仅子类可见
private[x]   // private,但对 x 可见
protected[x]

for 循环

for( var x <- Range )       // "<-" 表示赋值
for( a <- 1 to 10)          // 包含 10
for( a <- 1 until 10)       // 不含 10
for( a <- 1 to 3; b <- 1 to 3)

for( var x <- List )
for( a <- List(1,2,3,4,5,6) )

// 条件过滤
for( a <- List
     if a != 3; if a < 8 )

// yield
var retVal = for{ var a <- List
     if a != 3; if a < 8
}yield a
for( a <- retVal){
    println( "Value of a: " + a );
}

函数声明

def functionName ([参数列表]) : [return type]
def addInt( a:Int, b:Int ) : Int = {...}
def printMe( ) : Unit = {...}              // 无返回值
def printStrings( args:String* ) = {...}   // 可变长参数
def addInt( a:Int=5, b:Int=7 ) : Int = {...}    // 默认参数

传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部。

传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部。

区别就在于事先计算表达式还是到被调用的地方才计算。

def time() = { System.nanoTime }
def delayed( t: => Long ) = {...}      // "=>" 表示传名,t 是表达式,Long 是结果类型
delayed(time());

偏应用函数:

def log(date: Date, message: String) = {...}
val logWithDateBound = log(date, _ : String)    // 第一个绑定,用下划线替代缺失的参数列表
logWithDateBound("msg")

指定函数参数名:

def printInt( a:Int, b:Int ) = {...}
printInt(b=5, a=7)

函数式编程(参数和返回结果可以是函数):

def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString() + "]"      // 泛型
println( apply( layout, 10) )

匿名函数:

var inc = (x:Int) => x+1
var mul = (x: Int, y: Int) => x*y

柯里化(Currying):

def add(x:Int,y:Int) = x+y       // add(1,2)。普通函数
def add(x:Int)(y:Int) = x+y      // add(1)(2)。柯里化。其实调用了两次普通函数
def add(x:Int)=(y:Int)=>x+y      // 效果同上

闭包:

object Test {  
   //...
   var factor = 3  
   val multiplier = (i:Int) => i * factor  
}

字符串

buf += 'a'         // 连接字符
buf ++= "bcdef"    // 连接字符串
var len = buf.length()
println("msg")
str1.concat(str2)  // 连接
var str = "str1" + "str2"
var fs = printf("format: " + "%f %d %s", floatVar, intVar, stringVar)  // 返回值而不是输出

数组

var z:Array[String] = new Array[String](3)
var z = new Array[String](3)
var z = Array("Runoob", "Baidu", "Google")
z(0) = "Runoob"; z(1) = "Baidu"; z(4/2) = "Google"

var myList = Array(1.9, 2.9, 3.4, 3.5)
for ( x <- myList ) {...}                        // 遍历元素
for ( i <- 0 to (myList.length - 1)) {...}       // 遍历索引

var myMatrix = ofDim[Int](3,3)    // 多维数组。函数 ofDim[T]() 创建定长数组。
myMatrix(i)(j) = j

var myList3 = concat(myList1, myList2)   // 合并数组
var myList = range(10, 20, 2)            // (strat,end,step)

Collection

Collection 类型:List、Set、Map、Tuple、Option(类似枚举)、Iterator。

val x = List(1,2,3,4)
var x = Set(1,3,5,7)
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)
val x = (10, "Runoob")    // 元组
val x:Option[Int] = Some(5)

List

val empty: List[Nothing] = List()   // 空列表
val empty = Nil    // 空列表
val dim: List[List[Int]] = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 1))  // 二维列表

val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))    // List(Runoob, Google, Baidu)
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))    // List(1, 2, 3, 4)
val dim = (1 :: (0 :: (0 :: Nil))) ::
          (0 :: (1 :: (0 :: Nil))) ::
          (0 :: (0 :: (1 :: Nil))) :: Nil

list.head      // 首元素
list.tail      // 尾元素
list.isEmpty
list.reverse

List.concat(list1, list2)   // 连接
var list = list1 ::: list2  // 连接
var list = list1.:::(list2)  // 连接

val num = List.fill(10)(2)   // 填充 10 个 2

List.tabulate(4)(n => n * n)   // List(0, 1, 4, 9)
List.tabulate(3,4)(_*_)   // List(List(0, 0, 0, 0), List(0, 1, 2, 3), List(0, 2, 4, 6))

var x = List(1)     // List(1)
var y = 2 +: x      // List(2, 1)
var y = x :+ 3      // List(1, 3)

list.exists(s => s == "Hah")
list.filter(s => s.length == 3)

Set

Map

Tuple

Option

Iterator

val it = Iterator("Baidu", "Google", "Runoob", "Taobao")
while (it.hasNext) {...}
it.max
it.min
it.size     // 调用后 size 变 0
it.length   // 同 size

类和对象

定义类 & 实例化:

class Foo (x:Int, y:Int) {}
val foo = new Foo(10, 20)

继承:

// override val x 表示重写父类的字段
// 只能继承一个父类
class Bar (override val x:Int, override val y:Int, val z:Int) extends Foo(x,y) {}

重写非抽象方法:

class Person {
  var name = ""
  // override 表示非抽象方法
  override def toString = getClass.getName + "[name=" + name + "]"
}

class Employee extends Person {
  var salary = 0.0
  // override 表示重写非抽象方法
  override def toString = super.toString + "[salary=" + salary + "]"
}

单例模式:

// 用 object 就是单例
object Test {}

特征(Trait)

类似 Java 中的抽象类,特征可以多重继承。

trait Equal {
  def isEqual(x: Any): Boolean
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Point(xc: Int, yc: Int) extends Equal {
  def isEqual(obj: Any) = ...
}

模式匹配(match-case)

   def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      case y: Int => "scala.Int"
      case _ => "many"              // _ 相当于 default
   }

样例类:

object Test {
  def main(args: Array[String]) {
    //...
    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) => println("Age:" +age+ "year, name:" +name+ "?")
        }
    }
  }
  // 样例类
  case class Person(name: String, age: Int)
}

正则表达式

val pattern = "Scala".r        // 用 str.r() 构造 Regex 对象
val str = "Scala is Scalable and cool"
println(pattern findFirstIn str)    // 找到首个匹配项。findAllIn

val pattern = new Regex("(S|s)cala")
val pattern = "(S|s)cala".r

println((pattern findAllIn str).mkString(","))   // 用逗号连接返回结果
println(pattern replaceFirstIn(str, "Java"))    // 替换第一个匹配项。replaceAllIn

异常处理

抛出异常:

throw new IllegalArgumentException

异常处理:

import java.io.FileNotFoundException
import java.io.IOException

try {
  //...
} catch {
  case ex: FileNotFoundException => {...}
  case ex: IOException => {...}
} finally {
  //...
}

文件 I/O

// 写文件
import java.io._
val writer = new PrintWriter(new File("test.txt" ))
writer.write("hello")
writer.close()

// 读文件
import scala.io.Source
Source.fromFile("test.txt" ).foreach{
  print
}

// 读终端
print("Input: " )
val line = Console.readLine

概念

Class 和 Object 的区别

Class 是抽象的,不占内存,可以带参数。

Object 是(类的)实体,占用内存,不能带参数。

用 Object 就表示单例模式。

伴生对象、伴生类

当一个 Object 和 Class 同名时(当然必须在同一个文件),称它们是伴生的(companion),分别叫做伴生对象和伴生类。

// 伴生类
class Marker private(val color:String) {...}

// 伴生对象
object Marker{
  // 可以访问伴生类的私有属性和方法
  def main(args: Array[String]) {...}
}

多重继承

一个类最多只能继承一个父类,但是可以继承多个特征,都用 extends 关键字。

单例模式

样例类(case classes)

是一种特殊的类,经过优化以用于模式匹配。

提取器(Extractor)

当实例化对象时,会自动调用类的 apply() 方法,比如传入 name 和 age 返回一个 Person 对象。那么自然还有一个对应的反函数 unapply(),传入 Person 对象就返回当初创建它的参数,即 name 和 age。这个 unapply() 函数就叫提取器,能从对象中提取出创建它的参数。

技巧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment