scala中的符号问题

1.集合操作

原文:https://blog.csdn.net/datadev_sh/article/details/79854273

val a = List(1,2,3)
val b = List(4,5,6)

符号操作结果位置解释
::a :: bList(List(1, 2, 3), 4, 5, 6)前插把a当成一个元素,前插到b集合
+:a +: bList(List(1, 2, 3), 4, 5, 6)前插同上
:+a :+ bList(1, 2, 3, List(4, 5, 6))后插把b当成一个元素,后插到a集合
++a ++ bList(1, 2, 3, 4, 5, 6)拼接a和b集合顺序合并
++:a ++:bList(1, 2, 3, 4, 5, 6)拼接同上
:::a::::bList(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块钱!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值