Preface
本人的学习笔记,价值有限,仅供参考
本文受众:Java选手
目标:上手scala写代码,即,并不深入这门语言的以下方面:
- 如何与各种编程模式融合(参照设计模式和编码大全);
- 某一种集合数据结构(比如list)底层是如何实现;
- 多线程模型(结合os的进程线程模型)和内存模型(都知道scala用的是JVM,基于此可以比较方便理解);
- 语法糖(看到啥学啥,随缘用);
- 网络编程的底层实现;
- etc...
而是从以下几个方面入手,停留在用的层面:
- 基本语法:了解有哪些保留字和操作符(扫一眼并收藏为书签)、怎么声明变量、变量的作用域、基础数据类型以及类型转换; 循环、if-else怎么写、函数怎么定义和调用、代码结构如何抽象(java的类、go的package); 字符串相关的,因为Java里的字符串坑比较多。
- 集合数据结构:数组、列表/链表、map/字典、set等的常用编码模式和需要避开的坑;
- 并发编程:简单了解多线程模型&如何使用、再看一些常用的模式
- 网络编程:常用的模式
- 编写单元测试的方式
- 编码规范
- 语言特性
- 处理异常
依此展开下文。
基本语法
HelloWorld
以一个HelloWorld程序+注释来说明scala的基本语法。see more:docs.scala-lang.org/cheatsheets…
package com.example // package的定义,和Java一样可以import package或者import Class,不同点在于Java用*,scala用_,还可以用{Class1, Class2}引入一个包下面的多个class
// 定义一个object,object的概念和Java的object相同,但这种定义方式有点像Java的匿名内部类
object ScalaHelloWorldWithMain {
// 定义main函数,和Java一样是程序入口
def main(args: Array[String]): Unit = {
println("Hello World")
println("local: " + square(2))
}
// 定义函数
def square(x: Int) = x * x
// 可以直接调用函数,这里和Java不一样,Java必须加一个static,声明为static代码块
println("global: ", square(2))
}
也可以继承App类,这样就不用显式地声明main函数(参照App类的注释):
object ScalaHelloWorldWithApp extends App {
println("Hello World")
}
scala中的代码结构&如何在idea中创建scala项目(在已经安装scala SDK和scala plugin的前提下,如果没有装scala plugin,调用scala内置函数idea会标红报错)参照:docs.scala-lang.org/getting-sta…
基础数据类型
数字
- Char (16-bit unsigned Unicode character)
- Byte (8-bit signed value)
- Short (16-bit signed value)
- Int (32-bit signed value)
- Long (64-bit signed value)
- Float (32-bit IEEE 754 single-precision floating-point value)
- Double (64-bit IEEE 754 single-precision floating-point value)
and:
- Boolean (can have true or false values)
- Unit (carries no meaningful information)
**变量声明:**通过val关键字声明常量或者var关键字声明变量,也可以不声明关键字,让scala通过value做类型推断。
语法:val or val VariableName : DataType = [Initial Value]
muliple assignments:val (myVar1: Int, myVar2: String) = Pair(40, "Foo")或者val (myVar1, myVar2) = Pair(40, "Foo")
// 定义number类型
// byte
val b1: Btye = 100
// float & double
val f1 = 12.05f
val d1 = 12.3496067 // 隐式声明为double
var d2 = 12.3496067D // 显式声明为double
// 通过int的字面量推断类型
val i5 = 1234
val i6 = 0xAFBF // 16进制
val l4 = 1234L
val l3 = 0xCAFEBABEL // 16进制也要在末尾加L
字符
Char,声明语法和Java一样
处理字符串
语法和Java一样
在println中使用变量:docs.scala-lang.org/overviews/s…
类型转换
调用toType func,比如:
-
Int -> Byte,调用toByte
val aByteSizedInteger = 127 val byteFromInt = aByteSizedInteger.toByte //注意数据越界 -
String -> Int,调用toInt
val c4: Char = '@' val symbolToInt = c4.toInt // 可以转,按照编码 val stringToInt = "ABC".toInt // 不能转,会报错java.lang.NumberFormatException: For input string: "ABC"
todo:extend: Any和AnyVal
类和对象
概念和语法
类和对象的概念和Java中的一样,参考:www.runoob.com/scala/scala… 主要不同点:
-
定义class可以带参数(感觉像是把构造函数放在这儿了的意思),一个带参数的scala class等同于定义了私有变量且带getter和setter以及constructor的Java类;可以用val定义constructor参数,会变成不能修改的变量; 依然可以定义多个构造函数,scala称之为辅助构造函数(auxiliary constructor),语法和Java稍微不一样:
def this(firstName: String) = { this(firstName, "defaultLastName") } -
一个scala文件可以定义多个class;
-
scala的class没有public、private、protected这类声明;
-
接口和继承:(见下面的Scala traits)
-
Java的static vs. Scala的object
示例代码:
// 这个例子定义了可以在类中做的所有事情
// todo:之后可以关注类的初始化过程
class ScalaClassSample(var firstName: String, var lastName: String) {
println("the constructor begins")
// 默认的access域是public
var age = 0
// 定义类私有变量
private val HOME = System.getProperty("JAVA_HOME")
def this(firstName: String) = {
this(firstName, "defaultLastName")
}
// 定义类方法
// 和Java一样所有类都是Object的子类,都有toString()方法?
override def toString(): String = s"$firstName $lastName is $age years old"
// Unit,有点像void?
def printHome(): Unit = println(s"HOME = $HOME")
// this关键字和Java的this一样?
def printFullName(): Unit = println(this)
printHome()
printFullName()
println("you've reached the end of the constructor")
}
// 伴生对象
object ScalaClassSample {
def main(args: Array[String]): Unit = {
val sample = new ScalaClassSample("jack", "hook")
println(sample.toString())
println("age is:" + sample.age)
// age是public变量,设置它的值
sample.age = 36
println("age is:" + sample.age)
}
}
todo:伴生对象
docs.scala-lang.org/overviews/s…
定义枚举
Scala traits
Scala traits可以当作Java的接口和抽象类使用。scala也有抽象类,之后再说明什么时候应该用抽象类而不是Scala traits。
实现接口方法或者重写父类的抽象方法可以不用override关键字,重写父类的非抽象方法则需要。
可以动态混合特征(Mixing traits in on the fly,谷歌翻译),我理解为在new对象的时候再声明它继承了哪些类。
定义一个接口:
trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}
定义一个抽象类:
trait Pet {
def speak() = println("Yo")
def comeToMaster(): Unit
}
实现多个接口:
extends第一个trait; with 剩下的traits。
class Dog extends TailWagger with Speaker with Runner {
// 实现接口方法或者重写父类的抽象方法可以不用override关键字
def startTail(): Unit = println("tail is wagging")
override def stopTail(): Unit = println("tail is stopped")
// 甚至不需要return
override def speak(): String = "Woof!"
override def startRunning(): Unit = println("running")
}
动态混合特征(Mixing traits on the fly):
class Dog(name: String)
object ScalaTraits extends App {
val d = new Dog("Fido") with TailWagger with Runner
println(d.startRunning())
println(d.stopRunning())
}
抽象类
Scala 还有一个抽象类的概念,类似于 Java 的抽象类。但是因为特征是如此强大,你很少需要使用抽象类。实际上,您只需要在以下情况下使用抽象类:
- 您想创建一个需要构造函数参数的基类
- 您的 Scala 代码将从 Java 代码中调用
(抄自:docs.scala-lang.org/overviews/s…
变量
**声明:**在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量
参考:www.runoob.com/scala/scala…
**作用域:**Scala中的访问修饰符和Java一样,不同之处在于Scala只给函数和变量声明,没有给class;
函数
声明语法:def funcName([params1: Type]): [return type] = {}
如果不写函数体,只定义函数签名,会隐式地声明一个抽象方法。
**函数/方法调用:**函数直接调用;instance.funcName调用方法
Scala&函数式编程
Scala为函数式编程提供的features:
- 纯函数(pure functions)
- 传递函数(passing functions around)
纯函数
In Functional Programming, Simplified, Alvin Alexander defines a pure function like this:
- The function’s output depends only on its input variables
- It doesn’t mutate any hidden state
- It doesn’t have any “back doors”: It doesn’t read data from the outside world (including the console, web services, databases, files, etc.), or write data to the outside world
As a result of this definition, any time you call a pure function with the same input value(s), you’ll always get the same result. 理解为不包含任何隐藏的状态和后门(不读写任何外部存储、不改变外部变量值),函数被调用后的输出只受其输入变量影响。典型的纯函数是math库相关函数。 定义:
纯函数是仅依赖于其声明的输入及其内部算法来产生其输出的函数。它不会从“外部世界”(函数范围之外的世界)读取任何其他值,也不会修改外部世界中的任何值。
以上描述是为了说明Scala不仅提供了许多纯函数供调用,且提供方便地编写纯函数的能力。
传递函数
函数可以作为参数传递,且传的时候可以是匿名函数。
scala中的匿名函数 除了'_'比较让人疑惑之外,其他的都比较像Java的lambda表达式
-
匿名函数作为传递函数的写法:
val ints = List(1,2,3) val doubledInts = ints.map(_ * 2)doubleInts将会是一个List(2,4,6);'_'在scala中类似一个通配符的概念,它在不同的场景中可以被解释为不同的意思,在这里的意思是表示列表ints中的所有元素。
上面的代码也可以写成:
val doubledInts = ints.map((i: Int) => i * 2) // 这个比较像Java的lambda表达式 val doubledInts = ints.map(i => i * 2) -
定义匿名函数赋值给变量:
var myfc1 = (str1:String, str2:String) => str1 + str2 -
使用匿名函数遍历集合&filter:
val multiplier = 3 val ints = List(1, 2, 3, 4) val doubled = ints.map(_ * multiplier) assert(doubled == List(3, 6, 9, 12)) val ints2 = List(1,2,3,4,5) val actual = ints2.filter(_ > 2) assert(actual == List(3,4,5))
处理空值
函数式编程不使用空值,空值的替代品是Option、Some和None三个类,Some和None是Option的子类,因此代码可以这样编写:
def toInt(s: String): Option[Int] = {
try {
Some(Integer.parseInt(s.trim))
} catch {
case e: Exception => None
}
}
验证结果: 通过contains/isEmpty方法或match表达式;contains/isEmpty用于比较Option、Some、None,等同于match表达式(注释是这么说的)
var handle = new ScalaFuncExceptionValHandle()
val a = handle.toInt("1")
a match {
case Some(1) => println("toInt(1) return 1")
}
// contains/isEmpty方法专用于比较Option、Some、None,等同于match表达式
assert(a.contains(1))
val b = handle.toInt("foo")
b match {
case None => println("toInt(\"foo\") return None")
}
assert(b.isEmpty)
可以认为Option是一个容器,None是一个空容器(然而这对我写代码有什么用呢?所以并不详细看了)
流程控制语句
nothing special except match phrase & for with yield
所有流程控制语句的文档index:docs.scala-lang.org/overviews/s…
有try-catch finally,和Java一样
集合数据结构
todo
并发编程
todo
网络编程
todo