《快学Scala》(英文版:《Scala for the Impatient》),代码已传github:
https://github.com/vernonzheng/scala-for-the-Impatient
书为第一版。scala为2.11.4,jdk1.7.45,操作系统Mac OS X Yosemite 10.10.1。
第十五章 注解
15.1
编写四个JUnit测试用例,分别使用带或不带某个参数的@Test注解。用JUnit执行这些测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import org.junit.Test class ScalaTest { @Test def test1(){ print("test1") } @Test(timeout = 1L) def test2(){ print("test2") } }
|
15.2
创建一个类的示例,展示注解可以出现的所有位置。用@deprecated作为你的示例注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @deprecated class Test{ @deprecated val t = _; @deprecated(message = "unuse") def hello(){ println("hello") } } @deprecated object Test extends App{ val t = new Test() t.hello() t.t }
|
15.3
Scala类库中的哪些注解用到了元注解@param,@field,@getter,@setter,@beanGetter或@beanSetter?
略
15.4
编写一个Scala方法sum,带有可变长度的整型参数,返回所有参数之和。从Java调用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import scala.annotation.varargs class Test { @varargs def sum(nums: Int*): Int = { nums.sum } } /** Test2.java class Test2 { public static void main(String[] args){ Test t = new Test(); System.out.println(t.sum(1,2,3)); } } **/
|
15.5
编写一个返回包含某文件所有行的字符串的方法。从Java调用该方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import io.Source class Test5{ def read()={ Source.fromFile("test.txt").mkString } } /*** Test2.java public class Hello { public static void main(String[] args){ Test5 t = new Test5(); System.out.println(t.read()); } } ***/
|
15.6
编写一个Scala对象,该对象带有一个易失(volatile)的Boolean字段。让某一个线程睡眠一段时间,之后将该字段设为true,打印消息,然后退出。而另一个线程不停的检查该字段是否为true。如果是,它将打印一个消息并退出。如果不是,则它将短暂睡眠,然后重试。如果变量不是易失的,会发生什么?
没区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import concurrent.ops.spawn object Test06{ @volatile var value = false } spawn { Thread.sleep(100); Test06.value = true println("Thread1: setting value to TRUE!") } spawn { while(!Test06.value) Thread.sleep(20); println("Thread2: value is TRUE!") }
|
15.7
给出一个示例,展示如果方法可被重写,则尾递归优化为非法
1 2 3 4 5 6 7 8 9 10
| import annotation.tailrec object Test extends App{ @tailrec def sum2(xs : Seq[Int],partial : BigInt) : BigInt = { if (xs.isEmpty) partial else sum2(xs.tail,xs.head + partial) } println(sum2(1 to 1000000,0)) }
|
15.8
将allDifferent方法添加到对象,编译并检查字节码。@specialized注解产生了哪些方法?
1 2 3
| object Test{ def allDifferent[@specialized T](x:T,y:T,z:T) = x != y && x!= z && y != z }
|
javap Test$得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public final class Test$ extends java.lang.Object{ public static final Test$ MODULE$; public static {}; public boolean allDifferent(java.lang.Object, java.lang.Object, java.lang.Ob ject); public boolean allDifferent$mZc$sp(boolean, boolean, boolean); public boolean allDifferent$mBc$sp(byte, byte, byte); public boolean allDifferent$mCc$sp(char, char, char); public boolean allDifferent$mDc$sp(double, double, double); public boolean allDifferent$mFc$sp(float, float, float); public boolean allDifferent$mIc$sp(int, int, int); public boolean allDifferent$mJc$sp(long, long, long); public boolean allDifferent$mSc$sp(short, short, short); public boolean allDifferent$mVc$sp(scala.runtime.BoxedUnit, scala.runtime.Bo xedUnit, scala.runtime.BoxedUnit); }
|
15.9
Range.foreach方法被注解为@specialized(Unit)。为什么?通过以下命令检查字节码:
javap -classpath /path/to/scala/lib/scala-library.jar scala.collection.immutable.Range
并考虑Function1上的@specialized注解。点击Scaladoc中的Function1.scala链接进行查看
首先来看Function1的源码
1 2 3 4 5 6 7
| ...... trait Function1[@specialized(scala.Int, scala.Long, scala.Float, scala.Double/*, scala.AnyRef*/) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double/*, scala.AnyRef*/) +R] extends AnyRef { self => /** Apply the body of this function to the argument. * @return the result of function application. */ def apply(v1: T1): R ......
|
可以看到Function1参数可以是scala.Int,scala.Long,scala.Float,scala.Double,返回值可以是scala.Unit,scala.Boolean,scala.Int,scala.Float,scala.Long,scala.Double 再来看Range.foreach的源码
1 2 3 4 5 6 7 8 9 10 11 12 13
| ...... @inline final override def foreach[@specialized(Unit) U](f: Int => U) { if (validateRangeBoundaries(f)) { var i = start val terminal = terminalElement val step = this.step while (i != terminal) { f(i) i += step } } } ......
|
首先此方法是没有返回值的,也就是Unit。而Function1的返回值可以是scala.Unit,scala.Boolean,scala.Int,scala.Float,scala.Long,scala.Double 如果不限定@specialized(Unit),则Function1可能返回其他类型,但是此方法体根本就不返回,即使设置了也无法获得返回值
15.10
添加assert(n >= 0)到factorial方法。在启用断言的情况下编译并校验factorial(-1)会抛异常。在禁用断言的情况下编译。会发生什么?用javap检查该断言调用
1 2 3 4 5 6 7 8 9 10
| object Test { def factorial(n: Int): Int = { assert(n > 0) n } def main(args: Array[String]) { factorial(-1) } }
|
编译报错
1 2 3 4 5 6 7 8 9 10
| Exception in thread "main" java.lang.AssertionError: assertion failed at scala.Predef$.assert(Predef.scala:165) at Test$.factorial(Test.scala:6) at Test$.main(Test.scala:11) at Test.main(Test.scala) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
|
禁用assert
-Xelide-below 2011
反编译此类javap -c Test$ 得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ...... public int factorial(int); Code: 0: getstatic #19; 3: iload_1 4: iconst_0 5: if_icmple 12 8: iconst_1 9: goto 13 12: iconst_0 13: invokevirtual #23; 16: iload_1 17: ireturn ......
|
参考:
《快学Scala》:http://book.douban.com/subject/19971952/
(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)