内容简介:本文摘录Scala语言的一些语法和关键概念,不成系统,可看做学习笔记罢。Scala程序员的平衡感:
本文摘录Scala语言的一些语法和关键概念,不成系统,可看做学习笔记罢。
类型推断
for (arg <- args)
中 arg
一定是val类型,循环中不能改变其值。
Scala程序员的平衡感:
- 崇尚val,不可变对象和没有副作用的方法
- 首先想到他们,只有在特定需要或权衡后才选择var,可变对象或者带副作用方法。
Scala 伴生对象,是一个单例对象,可以看做 Java 中可能用到的静态方法 工具 类
基本类型
任何方法都可以是操作符,任何操作符都是方法。
函数式对象
辅助构造器,关键词this指向当前执行方法被调用的对象实例
如果使用在构造器里的话,就是指正在构建的实例
辅助构造器使用 def this(..)
定义,每个Scala构造器调用终将结束于对主构造器的调用。因为主构造器是类的唯一入口点。
重载操作符,重载后仍然按照原来的优先级,比如* > +
字面量标识符 yield
可以作为一个变量/常量名
函数和闭包
本地函数:函数定义在函数中,本地函数可以随意访问包含它的函数的参数
函数字面量
例子:(x: Int) => x + 1
在
foreach
, filter
等许多函数中会使用到,x的类型往往可以被推断,所以通常也可写成: x => x + 1
函数字面量存在于源代码,而函数值作为对象存在于运行期。
更简单的,可以使用占位符语法,用下划线当做一个或者多个参数的占位符,只要每个参数在函数字面量内只出现一次即可,第n个下划线代表第n个参数
如filter(_ > 0)
,调用时,用参数来填补下划线,也即 filter(x > 0)
如 reduce(_ + _)
,调用时,分别填补,也即 reduce(l+r)
偏函数(部分应用函数),一个下划线代替所有参数
闭包:函数字面量中包含了自由变量的绑定,运行时必须捕获其绑定。
如 val addMore = (x: Int) => x + more
, more
是自由变量
注意,闭包是一个非常重要的概念,我们时常会想要在循环体,比如foreach,map中加入一些对外部变量的修改,这是我们在其他语言养成的习惯。
直觉上,Scala在运行时会捕获自由变量本身,而不是变量指向的值。
比如
(x: Int) => x + more
此时创建的闭包可以看到闭包外部对more的改变,同样,闭包对捕获变量做出的修改在闭包外部也可见,比如:
val someNumber = List(-11, -10, 0, 10) var sum = 0 someNumber.foreach(x => sum += x) scala> sum res: Int = -11
但是如果读者用过Spark的话,一定会了解到Spark的闭包和Scala的闭包是不一样的,原因就在于Spark是分布式环境下运行的。
这里 有Spark官方对closure的描述。
还是上面那个例子
var sum = 0 var rdd = sc.parallelize(someNumbers) // Wrong: Don't do this!! rdd.foreach(x => sum += x) println("Counter value: " + counter) // counter = 0, because driver cannot feel the change of counter
众所周知,在分布式环境下,rdd的操作形成一个闭包,闭包会先序列化,然后被调度到各个executor执行,且每个executor拿到的其实是序列化后的sum,相当于driver端的一个copy,executor对sum的操作对driver来说不可见,driver的sum对各个executor来说也不可见,所以在driver端,counter始终是0。这就是scala闭包和spark闭包概念的一个最大不同。
重复参数,即可变参数,尾部加 *
号即可,如:
def echo(args: String*)
args
其实是 Array[String]
类型,但是仍然不能真的传入一个 Array[String]
类型的参数,比如要传入 arr
,你需要 echo(arr: _*)
这么写,意思是告诉编译器把每个元素当参数而不是把arr当做单一参数。
尾递归:在最后一个动作调用自己的函数。注意只能是单纯的调用自己,不能有多余的表达式,也不能通过其它函数中转
Scala的核心:简洁,简洁,简洁!
控制抽象
柯里化,传名参数
组合与继承
组合指一个类持有另一个的引用,借助被引用的类完成任务。
不带参数,且没有副作用的方法可以不写括号
“脆基类”问题:意外的方法重写
多态的重新理解:父类型引用可以指向子类型对象 => 父类对象可有多种形式 => 多态
动态绑定:被调用的实际方法取决于运行期对象基于的类型
Scala 层级
所有类的父类是Any类,下辖两个子类,AnyRef(所有引用类的父类)和AnyVal(所有值类的父类)
底层有Nothing类和Null类,Null类是所有引用类的子类,不兼容子类型,而Nothing是所有类的子类。
scala的==对值类型为自然相等,对引用类型来说被视为equals方法的别名,equals初始定义为引用相等,但许多子类都会重写它以实现自然意义上的相等。
要比较引用相等,可以使用eq方法(反面是ne方法)
特质(trait)
特质类似Java中的接口,混入特质可以使用extends或者with
特质像是带有具体方法的Java接口,并且可以声明字段和维持状态值,特质可以做类定义所能做的事
但与类定义有两点不同:
1) 特质不能有参数(传递给主构造器)
2)super调用时动态绑定的
胖接口:拥有更多方法的接口
特质的一个用法就是把瘦接口变成胖接口
需要 排序 比较时,可以混入(mixin)Ordered特质
步骤:混入Ordered特质,实现compare方法,可以自动拥有大多数比较方法,但是不会有equals方法 => 类型擦除
特质的第二个用法:为类提供可堆叠的改变
混入多个特质,最右边的特质最先起作用
不同的组合,不同的次序混入特质,可以依靠少量的特质得到多个不同的类
特质线性化地解释super
特质,用还是不用?
1) 如果行为不会被重用,那做成具体类
2)如果要在多个不相关的类中重用,那就做成特质
3)如果希望从Java代码继承,那就是用抽象类 (只含有抽象成员的scala特质会被直接翻译成Java接口)
4)如果计划以编译后的方式发布,或者希望外部组织继承它,更倾向使用抽象类
5)如果效率很重要,倾向于使用类
包和引用
_root_
顶层包:所有你能写出来的顶层包都是 _root_
的成员,可以用 _root_.yourpack
来访问
scala应用灵活在于:
1)可以随处import
2)可以指对象或包
3)可以重命名或者隐藏
每个scala源文件都隐含引用java.lang包,scala包以及单例对象Predef
访问修饰符:protected比Java中的更加严格:仅限子类访问,同一包中的类不能访问
访问修饰符限定规则:
private[X] method/class
此类或方法对X下所有类和对象可见
protected[X] method/class
对此类或子类或修饰符所在的包,类或对象X可见 (?)
断言和单元测试
assert
:
assert(ele.width === 2)
三等号,如果不等,会报告 “3 dit not equal to 2”
或
expect (2) { ele.width }
intercept检查是否抛出了期待的异常
intercept(class[IllegalArgumentException]) { elem('x', -2, 3) }
scala中的一些测试方法:
1) ScalaTest
2) Suite:
3) JUnit
4) TestNG
5) Specs 规格测试, a should be … 具有描述部分和规格部分
ScalaCheck 属性测试,测试代码具有属性
样例类和模式匹配
样例类 case class CLSNAME(argA: argAType, ...)
最大的好处是他们可以支持模式匹配
模式有很多种,包括通配,常量模式,变量模式,构造器模式,序列模式,元组模式,类型模式,更高级的还有变量绑定
使用类型模式应注意类型擦除,擦除规则不适用于数组
编译器会为 case class
自动生成伴生对象
编译器也会为该伴生对象自动生成 apply
, unapply
方法
模式守卫
列表:List
List是协变的,意味着,如果S是T的子类,List[S]就是List[T]的子类,故而 List[Nothing]
是 List[String]
的子类,故可以 val List[String] = List()
::
元素与List连接,元素与元素连接
:::
List与List连接
计算长度 .length
方法需要遍历整个列表,所以如果判断长度为0的话最好使用 .isEmpty
方法
访问头部: init
方法,访问除了最后一个元素外的子列表, head
方法:访问第一个元素
访问尾部: last
方法,访问最后一个元素, tail
方法:访问除第一个元素外的列表
更一般的, drop
, take
方法
copyToArray
: 把列表元素复制到目标数组的一段连续空间
elements
方法:返回迭代器
其他的List高阶方法:
map,flatMap,foreach 过滤:filter,partition,find,takeWhile,dropWhile,span 论断:forall,exists 折叠:/: 和 :\ 翻转reverse,排序sortWith
List对象的方法:
List.apply,List.range,List.make(4, 'a') List.unzip 解除啮合 连接: List.flatten, List.concat
区别在于前者用列表的列表做参数,后者可以直接用多个列表作为参数(以可变参数的方式)
Scala类型推断
Scala采用局部的,基于流的类型推断算法
通常,一旦有需要推断多态方法类型参数的任务时,类型推断器只会参考第一个参数列表中所有的值参数类型,而不会参考之后的参数。
库方法设计原则:
如果需要把参数设计为若干非函数值即一个函数值的某种多态方法,需要把函数参数独自放在柯里化参数列表的最后面。
即,在柯里化方法中,方法类型仅取决于第一段参数。
同样的,一种快速解决类型错误问题的方法:
添加明确的类型标注
集合与映射
Set, Seq, Map -> Iterable SortedSet SynchronizedMap
如果元素数量不多,不可变集合比可变集合存储更紧凑,空间更加节省。
可变状态的对象
状态与var变量常常一起出现,但并不具有严格的关系。
类即使没有定义或继承var变量,也可以由于把方法调用传递给其他具有可变状态的对象而带有状态,(有点拗口)
类即使包含了var变量也可以仍是纯函数的
Scala中,对象的每个非私有的var类型成员变量都隐含定义了 getter
和 setter
方法。
getter
方法为 x
setter
方法为 x_
类中字段初始化为0/false/null,
var x = _
不可以省略 “= _”,否则var x: Float 为抽象变量,而不是未初始化的变量
类型参数化(重头戏)
类型参数化让我们能够编写泛型和特质
信息隐藏:
隐藏主构造器: private加载类名的后面,类参数列表的前面:
class Queue[T] private ( private val leading: List[T], private val tailing: List[T] )
那么如何构造对象呢?
方法1:定义辅助构造器
def this() = this(Nil, Nil) def this(elem: T*) = this(elem.toList, Nil)
方法2:在伴生对象中编写apply工厂方法
object Queue { def apply[T](xs: T*) = new Queue[T](xs.toList, Nil) }
(注意,scala没有全局方法,方法必须包含在类或对象中)
另一种信息隐藏的方法,直接把类本身通过暴露特质(trait)而隐藏掉
如果类或者特质声明时带类型参数,那么创建变量时也要制定具体的参数化的类型
如
trait Queue[T] { .. } Queue是特质, Queue[String]是类型
泛型:通过一个能够广泛适用的类或特质,定义了许多特定的类型
在scala中,泛型类默认是非协变的子类型化
如果要表明参数的子类型化是协变的,需要变成如下形式:
trait Queue[+T] { .. }
加上-号表示需要逆变的子类型化
只要泛型的参数类型被当做方法参数的类型,那么包含它的类或特质就有可能不能与这个类型参数一起协变
class Queue[+T] { def append(x: T) = ... }
下界和上界
def append[U>: T](x: U) = new Queue[U](leading, x :: tailing)
语法 U >: T
定义了T为U的下界,结果U必须是T的超类型,append的参数现在为U而不是T,返回类型也变成了 Queue[U]
,不过,自身即是自身的超类型又是自身的子类型,所以是类似小于等于的关系。
def orderedMergeSort[T <: Ordered[T]](xs: List[T]): List[T] = ...
语法 T <: Ordered[T]
定义了类型参数T具有上界 Ordered[T]
,即传递给 orderedMergeSort
的参数必须是 Ordered[T]
的子类型。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Swift语法快速入门(一)之 Swift基础语法
- 在ES6中使用扩展语法有什么好处?它与rest语法有什么不同?
- Python 基础语法
- go语法
- JPQL 语言语法
- reStructuredText简明语法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。