1.集合操作
原文:https://blog.csdn.net/datadev_sh/article/details/79854273
val a = List(1,2,3)
val b = List(4,5,6)
符号 | 操作 | 结果 | 位置 | 解释 |
---|---|---|---|---|
:: | a :: b | List(List(1, 2, 3), 4, 5, 6) | 前插 | 把a当成一个元素,前插到b集合 |
+: | a +: b | List(List(1, 2, 3), 4, 5, 6) | 前插 | 同上 |
:+ | a :+ b | List(1, 2, 3, List(4, 5, 6)) | 后插 | 把b当成一个元素,后插到a集合 |
++ | a ++ b | List(1, 2, 3, 4, 5, 6) | 拼接 | a和b集合顺序合并 |
++: | a ++:b | List(1, 2, 3, 4, 5, 6) | 拼接 | 同上 |
::: | a::::b | List(1, 2, 3, 4, 5, 6) | 拼接 | 同上 |
2.泛型中的语法糖: “<:” , “>:” , “<%” , “=:=” , “<:<” , “<%<”, “+T”, “-T”
原文:https://blog.csdn.net/bobozhengsir/article/details/13023023
2.1 上下界约束符号 <: 与 >:
def using[A <: Closeable, B](closeable: A) (getB: A => B): B =
try {
getB(closeable)
} finally {
closeable.close()
}
例子中A <: Closeable(java.io.Cloaseable)的意思就是保证类型参数A是Closeable的子类(含本类), >:反之。
2.2 协变与逆变符号+T与 -T
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。e.g. String => AnyRef
“逆变”则是指能够使用派生程度更小的类型。e.g. AnyRef => String
【+T】表示协变,【-T】表示逆变
2.2.1 协变
借用一个例子来理解:
当我们定义一个协变类型List[A+]时,List[Child]可以是List[Parent]的子类型。
当我们定义一个逆变类型List[-A]时,List[Child]可以是List[Parent]的父类型。
class Animal {}
class Bird extends Animal {}
class Consumer[T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird](new Bird)
val c2:Consumer[Animal] = c
}
c不能赋值给c2,因为Consumer定义成不变类型。
稍微改一下:
class Animal {}
class Bird extends Animal {}
class Consumer[+T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird](new Bird)
val c2:Consumer[Animal] = c
}
因为Consumer定义成协变类型的,所以Consumer[Bird]是Consumer[Animal]的子类型,所以它可以被赋值给c2
2.2.2 逆变
继续上面得例子:
class Animal {}
class Bird extends Animal {}
class Consumer[-T](t: T) {
}
class Test extends App {
val c:Consumer[Bird] = new Consumer[Bird](new Bird)
val c2:Consumer[Hummingbird] = c
}
这里Consumer[-T]定义成逆变类型,所以Consumer[Bird]被看作Consumer[Hummingbird]的子类型,故c可以被赋值给c2。
2.3 视界 <%
Scala还有一种视图绑定的功能,如:
class Bird {def sing = {}}
class Toy {}
class Consumer[T <% Bird]() {
def use(t: T) = t.sing
}
或者类型参数在方法上:
class Bird {def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
val c = new Consumer()
c.use(new Toy)
}
它要求T必须有一种隐式转换能转换成Bird,也就是 T => Bird,否则上面的代码会编译出错:
No implicit view available from Toy => Bird.
加入一个隐式转换,编译通过。
import scala.language.implicitConversions
class Bird {def sing = {}}
class Toy {}
class Consumer() {
def use[T <% Bird](t: T) = t.sing
}
class Test extends App {
implicit def toy2Bird(t: Toy) = new Bird
val c = new Consumer()
c.use(new Toy)
}
2.4 广义类型约束符号 =:=, <:<, <%<
这些被称为广义的类型约束。他们允许你从一个类型参数化的class或trait,进一步约束其类型参数之一。
符号 | 含义 |
---|---|
A =:= B | 表示 A 必须是 B 类型 |
A <:< B | 表示 A 必须是B的子类型 (类似于简单类型约束 <:) |
A <%< B | 表示 A 必须是可视化为 B类型, 可能通过隐式转换 (类似与简单类型约束 <%) |
例子:
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
结果:
scala> Foo("blah").getStringLength
res0: Int = 4
这个隐式的参数 evidence 由编译器提供,A =:=String表示证明A是String类型(PS:即使A可以隐式转换成String类型也不行),因此参数a就可以调用a.length 而编译器不会报错。
一旦我们使用其他不能转换成String类型的参数,就会报错,如下:
scala> implicit def charSeq2String(s: Seq[Char]) = s.mkString
charSeq2String: (s: Seq[Char])String
scala> Foo(Seq[Char]('a','b','c')).getStringLength
<console>:11: error: Cannot prove that Seq[Char] =:= String.
Foo(Seq[Char]('a','b','c')).getStringLength
^
3. 函数参数的传名调用(call-by-name,:=>)和传值调用(call-by-value,:)
原文:https://blog.csdn.net/asongoficeandfire/article/details/21889375
类似于值传递(call-by-value,传值调用,符号" :")和引用传递(call-by-name,传名引用,符号 “:=>”)
例子:举一个例子,假设有一只酒鬼,他最初有十元钱,每天喝酒都会花掉一元钱。设他有一个技能是数自己的钱,返回每天他口袋里钱的最新数目。
package com.doggie
object Drunkard {
//最开始拥有的软妹币
var money = 10
//每天喝掉一个软妹币
def drink: Unit = {
money -= 1
}
//数钱时要算上被喝掉的软妹币
def count: Int = {
drink
money
}
//每天都数钱
def printByName(x: => Int): Unit = {
for(i <- 0 until 5)
println("每天算一算,酒鬼还剩" + x + "块钱!")
}
//第一天数一下记墙上,以后每天看墙上的余额
def printByValue(x: Int): Unit = {
for(i <- 0 until 5)
println("只算第一天,酒鬼还剩" + x + "块钱!")
}
def main(args: Array[String]) = {
printByName(count)
printByValue(count)
}
}
我们使用成员变量money来表示酒鬼剩下的软妹币数量,每次发动drink技能就消耗一枚软妹币,在count中要计算因为drink消费掉的钱。我们定义了两种计算方式,printByName是传名调用,printByValue是传值调用。查看程序输出:
每天算一算,酒鬼还剩9块钱!
每天算一算,酒鬼还剩8块钱!
每天算一算,酒鬼还剩7块钱!
每天算一算,酒鬼还剩6块钱!
每天算一算,酒鬼还剩5块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!