Scala中那些令人头痛的符号
来源:互联网 发布:苏芒睡了多少明星知乎 编辑:程序博客网 时间:2024/04/30 10:27
Scala中符号语法糖
初学Scala看到那些稀奇古怪的符号(e.g. <: , >: , <% , =:= , <:< , <%<, +T, -T ),总让人摸不着头脑,Scala创造这些语法糖究竟是要做甚?再参详了几篇文章(具体见参考文献)后,作者终于可以摸着一些皮毛了,于是决定记录下来。
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的子类(含本类),语法“A <: B"定义了B为A的上界;同理相反的A>:B的意思就是A是B的超类(含本类),定义了B为A的下界。
其实<: 和 >: 就等价于java范型编程中的 extends,super(PS: 说起来C#中只有where A:B形似的上界约束,怎么没有下界约束呢?求高人指教)
2. 协变与逆变符号+T, -T
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。e.g. String => AnyRef
“逆变”则是指能够使用派生程度更小的类型。e.g. AnyRef => String
3. view bounds(视界) 与 <%
<%的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型
def method [A <% B](arglist): R = ...
等价于:
def method [A](arglist)(implicit viewAB: A => B): R = ...
或等价于:
implicit def conver(a:A): B = …def method [A](arglist): R = ...
<% 除了方法使用之外,class声明类型参数时也可使用:
scala> class A[T <% Int]defined class A
但无法对trait的类型参数使用 <%,
scala> trait A[T <% Int]<console>:1: error: traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'
4. 广义类型约束符号 =:=, <:<, <%<
这些被称为广义的类型约束。他们允许你从一个类型参数化的class或trait,进一步约束其类型参数之一。下面是一个例子:
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}
这个隐式的参数 evidence
由编译器提供,A =:=String表示证明A是String类型(PS:即使A可以隐式转换成String类型也不行),因此参数a就可以调用a.length 而编译器不会报错。
我们可以如下使用:
scala> Foo("blah").getStringLengthres0: Int = 4
一旦我们使用其他不能转换成String类型的参数,就会报错,如下:
scala> Foo(123).getStringLength<console>:10: error: Cannot prove that Int =:= String. Foo(123).getStringLength ^
scala> implicit def charSeq2String(s: Seq[Char]) = s.mkStringcharSeq2String: (s: Seq[Char])Stringscala> Foo(Seq[Char]('a','b','c')).getStringLength<console>:11: error: Cannot prove that Seq[Char] =:= String. Foo(Seq[Char]('a','b','c')).getStringLength ^
<:<
和 <%<
使用类似, 有细微不同:
A =:= B
表示 A 必须是 B 类型A <:< B
表示 A 必须是B的子类型 (类似于简单类型约束<:
)A <%< B
表示 A 必须是可视化为 B类型, 可能通过隐式转换 (类似与简单类型约束<%
)
5. 传名调用(call-by-name)符号: => type
传名调用 (Call by name)[编辑]
在“传名调用”求值中,根本就不求值给函数的实际参数 — 而是使用避免捕获代换把函数的实际参数直接代换入函数体内。如果实际参数在函数的求值中未被用到,则它永不被求值;如果这个实际参数使用多次,则它每次都被重新求值。
传名调用求值超过传值调用求值的优点是传名调用求值在一个值存在的时候总是生成这个值,而传名调用可能不终止如果这个函数的实际参数是求值这个函数所不需要的不终止计算。反过来说,在函数的实际参数会用到的时候传名调用就非常慢了,这是因为实践中几乎总是要使用如 thunk 这样的机制。
传需求调用 (Call by need)
“传需求调用”是传名调用的记忆化版本,如果“函数的实际参数被求值了”,这个值被存储起来已备后续使用。在“纯”(无副作用)设置下,这产生同传名调用一样的结果;当函数实际参数被使用两次或更多次的时候,传需求调用总是更快。
object TargetTest2 extends Application { def loop(body: => Unit): LoopUnlessCond = new LoopUnlessCond(body) protected class LoopUnlessCond(body: => Unit) { def unless(cond: => Boolean) { body if (!cond) unless(cond) } } var i = 10 loop { println("i = " + i) i -= 1 } unless (i == 0)}上面的程序运行结果是
i = 10i = 9i = 8i = 7i = 6i = 5i = 4i = 3i = 2i = 1
6.参考文献
- Scala中那些令人头痛的符号
- 运维日记003-那些曾经令人头痛的乱码
- 令人头痛的PetShop
- 令人头痛的代码
- 详解C++中令人头痛的&和*操作符
- 令人头痛的ExtJS日期时间控件
- 令人头痛的tomcat启动问题
- 团队合作中一起共事的那些头痛事
- 解决Maven项目中令人头痛的无错误但是有小红叉问题
- Scala那些奇怪的符号(三)“<%” 和 “:”
- 令人头痛的Header Files和Source Files
- C#调用C++ DLL令人头痛的两件事
- CIO如何处理令人头痛的项目积压
- 令人头痛的linux编译错误 “未知存储大小"
- TCP/IP中那些令人豁然开朗的说明
- 嵌入式软件开发中那些令人难忘的bug
- Scala 的那些奇怪的符号 (一):“<:” 和 “>:” 作用及用法
- Scala的那些奇怪的符号(二) [+T]和[-T]
- [MySQL] 用mysqldump制作文本备份
- RewriteCond和13个mod_rewrite应用举例Apache伪静态之htaccess编写
- ※数据结构※→☆非线性结构(tree)☆============二叉搜索树(二叉查找树) 链式存储结构(tree Binary Search list)(二十五)
- vtigercrm 分析
- Ten Things You Need to Know About Indoor Positioning
- Scala中那些令人头痛的符号
- TailQueue详解
- Android KSOAP2调试(上传图片到服务器)
- SVNSubversion 用户权限管理
- MFC控件
- Twitter Storm开篇之作
- 如何管理Ubuntu启动项
- 简单的oracle查询语句
- c++支持课-----求根