摘要:本文汇总在 scala
中遇到各种符号操作符。
模式匹配中使用 @
这个 @ 的主要功能是在模式匹配中,匹配到一个模式后,但是在处理过程中,使用对象本身而不是匹配后的元素。
案例1:匹配 Some()
参考该 Overflow 回答
当我们匹配对象是否为 Some(x)
的时候,如果没有 @
那么最后我们在匹配中的值将是 Some(x)
中的 x
, 而如果加入 @
,那么最后匹配的将会是 Some(x)
It enables one to bind a matched pattern to a variable. Consider the following, for instance:
1 | val o: Option[Int] = Some(2) |
You can easily extract the content:
1 | o match { |
But what if you wanted not the content of Some, but the option itself? That would be accomplished with this:
1 | o match { |
Note that @ can be used at any level, not just at the top level of the matching.
案例2:API 匹配 Request
1 | def intent = { |
在处理 request
请求的时候,需要匹配请求的路径,然后还需要直接使用 requet
。
下划线 _ 的使用场景
import 通配符
1 | import org.apache.spark.SparkContext._ |
集合操作指代每一个元素
在所有的集合操作中都可以使用下划线指代集合内容。
1 | object Sample { |
在一个 Array
a
中筛出偶数,并乘以2
1 | a.filter(_%2==0).map(2*_) |
模式匹配使用
主要用于匹配通配的情形
1 | Some(5) match { case Some(_) => println("Yes") } |
其他案例
1 | val m = Map(1 -> 2,2 -> 4) |
变量初始化为 null
1 | object Sample { |
在这里,name
也可以声明为 null
,例:var name:String=null
。这里的下划线和 null
的作用是一样的。
:_*
参数序列处理
:_*
作为一个整体,告诉编译器你希望将某个参数当作参数序列处理!
例如,当函数接收的参数不定长的时候,假如你想输入一个队列,可以在一个队列后加入“:_*
”,因此,这里的“1 to 5”也可以改写为:“Seq(1,2,3,4,5)”。
1 | object Sample { |
元组访问成员
在元组中,可以用方法_1, _2, _3
访问组员。如 a._2
。其中点操作符可以用空格替代,也就是 a _2
。
1 | object Sample { |
匿名函数 =>
这个比较简单,因为 Java8
的 Lambda
已经广泛使用。
=>
匿名函数(Anonymous Functions): 表示创建一个函数实例。
比如:(x: Int) => x + 1
和如下JAVA方法表示的含义一样:
1 | public int function(int x){ |
可以这么理解:
- =>左边 是输入参数,
:
后面int
是参数类型 - =>右边 当作函数体, 类似匿名函数的
{}
模式匹配中配合 case
1 | val bools = Seq(true, false) |
集合遍历 <-
简单说这是一个集合遍历的方法:
1 | var list = Array(1,2,3,4) |
但是在 for
推导式中,这个符号表示生成器,其解释比循环更加复杂,是一个 scala
的语法糖,最后会被解释为一系列的容器操作:map
和 flatMap
等。参考另一篇详细介绍 Scala For
推导式的文章。
集合拼接 ++= —= 等操作
++= 用于拼接容器,而 += 用于拼接元素。
WHAT IT IS WHAT IT DOES
加法:
- xs += x 把元素 x 添加到集合 xs 中。该操作有副作用,它会返回左操作符,这里是 xs 自身。
- xs += (x, y, z) 添加指定的元素到集合 xs 中,并返回 xs 本身。(同样有副作用)
- xs ++= ys 添加集合 ys 中的所有元素到集合 xs 中,并返回 xs 本身。(表达式有副作用)
- xs add x 把元素 x 添加到集合 xs 中,如集合 xs 之前没有包含 x,该操作返回 true,否则返回 false。
移除:
- xs -= x 从集合 xs 中删除元素 x,并返回 xs 本身。(表达式有副作用)
- xs -= (x, y, z) 从集合 xs 中删除指定的元素,并返回 xs 本身。(表达式有副作用)
- xs —= ys 从集合 xs 中删除所有属于集合 ys 的元素,并返回 xs 本身。(表达式有副作用)
- xs remove x 从集合 xs 中删除元素 x 。如之前 xs 中包含了 x 元素,返回 true,否则返回 false。
- xs retain p 只保留集合 xs 中满足条件 p 的元素。
- xs.clear() 删除集合 xs 中的所有元素。
更新:
- xs(x) = b ( 同 xs.update(x, b) )参数 b 为布尔类型,如果值为 true 就把元素x加入集合 xs,否则从集合 xs 中删除 x。
克隆:
- xs.clone 产生一个与 xs 具有相同元素的可变集合。
冒号操作符
:::运算符
三个冒号表示List的连接操作,比如:
1 | val a = List(1,2) |
::两个冒号
两个冒号表示普通元素与List的连接操作,比如:
1 | val a=1 |
元组操作 ->
scala中元组含义:
元组是不同类型的值聚集线程的列表
通过将多个值使用小括号括起来,即表示元组
scala中元组与数组区别
数组中元素 数据类型必须一样,但是元组数据类型可以不同。
1 | val first = (1,2,3) // 定义三元元组 |
上下界约束符 <: 与 >:
这对符号个人觉得是里面最好理解的了,这对符号用于写范型类/函数时约束范型类型。
1 | def using[A <: Closeable, B](closeable: A) (getB: A => B): B = |
例子中 A <: Closeable(java.io.Cloaseable)
的意思就是保证类型参数 A
是 Closeable
的子类(含本类)。
语法“A <: B
“定义了B为A的上界;同理相反的 A >: B
的意思就是A是B的超类(含本类),定义了B为A的下界。
其实 <:
和 >:
就等价于java范型编程中的 extends
,super
。
协变与逆变符号+T, -T
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。e.g. String => AnyRef
“逆变”则是指能够使用派生程度更小的类型。e.g. AnyRef => String
【+T】表示协变,【-T】表示逆变
参考 《Scala 程序设计第2版》 2.13 抽象类型和参数化类型:
Scala 支持参数化类型,与 Java 中的泛型十分类似。这两个术语,但 Scala 社区中多使用“参数化类型”, Java 社区中常用泛型一词。)在语法上, Java 使用尖括号( <… >
),而 Scala 使用方括号( [… ]
),因为在 Scala 中 <
和 >
常用作方法名。
例如,字符串列表可以声明如下:
1 | val strings: List[String] = List("one", "two", "three") |
由于我们可以在集合 List[A]
中使用任何类型作为类型 A,这种特性被称为参数多态。在方法 List 的通用实现中,允许使用任何类型的实例作为 List 的元素。
A 之前的 +
表示:如果 B
是 A
的子类,则 List[B]
也是 List[A]
的子类型,这被称为协类型。协类型很符合直觉,如果我们有一个函数 f(list: List[Any])
,那么传递 List[String]
给这个函数,也应该能正常工作。
如果类型参数前有 -
,则表示另一种关系:如果 B
是 A
的子类型,且 Foo[A]
被声明为 Foo[-A]
,则 Foo[B]
是 Foo[A]
的父类型(称为逆类型)。这一机制没那么符合直觉,我们将在参数化类型中与参数化类型的其他细节一起解释这一点。
Scala 还支持另一种被称为“抽象类型”的抽象机制,它可以运用在许多参数化类型中,也能够解决设计上的问题。然而,尽管两种机制有所重合,但并不冗余,两种机制对不同的设计问题各有优势与不足。
view bounds(视界) 与 <%
<%
的意思是“view bounds”(视界),它比<:
适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型
1 | def method [A <% B](arglist): R = ... |
等价于
1 | def method [A](arglist)(implicit viewAB: A => B): R = ... |
表示 A 可以视为类型 B
案例2:
视界,就像类型边界,要求存在一个能够将某类型转换为指定类型的函数。你可以使用 <% 指定类型限制,例如:
1 | scala> class Container[A <% Int] { def addIt(x: A) = 123 + x } |
这是说 A 必须“可被视作” Int 。让我们试试。
1 | scala> (new Container[String]).addIt("123") |
<% 除了方法使用之外,class声明类型参数时也可使用:
1 | scala> class A[T <% Int] |
但无法对trait的类型参数使用 <%,
1 | scala> trait A[T <% Int] |
广义类型约束符号 =:=, <:<, <%<
方法可以通过隐式参数执行更复杂的类型限制。例如,List 支持对数字内容执行 sum,但对其他内容却不行。可是 Scala 的数字类型并不都共享一个超类,所以我们不能使用T <: Number。相反,要使之能工作,Scala的math库对适当的类型T 定义了一个隐含的 Numeric[T]
。 然后在 List 定义中使用它:
1 | sum[B >: A](implicit num: Numeric[B]): B |
如果你调用 List(1,2).sum()
,你并不需要传入一个 num 参数;它是隐式设置的。但如果你调用 List("whoop").sum()
,它会抱怨无法设置 num。
在没有设定陌生的对象为 Numeric 的时候,方法可能会要求某种特定类型的“证据”。这时可以使用以下类型-关系运算符:
A =:= B
A 必须和 B相等A <:< B
A 必须是 B的子类A <%< B
A 必须可以被看做是 B
(如果你在尝试使用 <:< 或者 <%< 的时候出错了,那请注意这些符号在 Scala 2.10 中被移除了。Scala School 里的例子仅能在 Scala 2.9.x 下正常工作。你可以使用新版本的 Scala,但可能会遇到错误。)
1 | scala> class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value } |
字符串插值 s"$c"
参考官方文档:字符串插值
s 字符串插值器
1 | val name="James" |
f 插值器
在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。当使用 f 插值器的时候,所有的变量引用都应当后跟一个printf-style格式的字符串,如%d。看下面这个例子:
1 | val height=1.9d |
raw 插值器
除了对字面值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。如下是个被处理过的字符串:
1 | scala>s"a\nb" |
Akka 相关特殊字符
send !
1 | case ArticleBody(uri, body) => //If we get the parsed article back, then we've just parsed it |
ask ?
1 | val future = pongActor ? "unknown" |