Scala練習(十五)
\1. 編寫四個Junit測試案例,分別使用帶或不帶某個函式的@Test註解。用 Junit執行這些測試。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import org.junit.Test class ScalaTest { @Test def test1() { println("test1") } @Test(timeout = 1L) def test2() { println("test2") } } |
\2. 建立一個類的示例,展示註解可以出現的所有位置。用@deprecated作為你的示例註解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@deprecated class Test { @deprecated val t = "unuse" @deprecated(message = "unuse") def hello() { println("hello") } } @deprecated object Test extends App { val t = new Test() t.hello } |
\3. Scala 類庫中的那些註解用到了元註解@param, @field, @getter, @setter, @beanGetter或@beanSetter?
1 |
略 |
\4. 編寫一個Scala方法sum,帶有可變長度的整型引數,返回所有引數之和。從Java呼叫該方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import annotation.varargs class sumTest { @varargs def sum(nums: Int*): Int = { nums.sum } } public class Hello { public static void main(String[] args) { sumTest t = new sumTest(); System.out |
\5. 編寫一個返回包含某檔案所有行的字串的方法。從Java呼叫該方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import io.Source object FileTest { def read(path: String): String = { Source.fromFile(path).mkString } } public class FileRead { public static void main(String[] args) { System.out.println(FileTest.read("/tmp/a.txt")); } } |
\6. 編寫一個Scala物件,該物件帶有一個易失(volatile)的Boolean欄位。讓某一個執行緒睡眠一段時間,之後將該欄位設為true, 列印訊息,然後退出。而另一個執行緒不停地檢查該欄位是否為true。 如果是,它將列印一個訊息並退出。如果不是,它將短暫睡眠,然後重試。如果變數不是易失的,會發生什麼?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import scala.actors.Actor class T1(obj: Obj) extends Actor { def act() { println("T1 is waiting") Thread.sleep(5000) obj.flag = true println("T1 set flag = true") } } class T2(obj: Obj) extends Actor { def act() { var f = true while (f) { if (obj.flag) { println("T2 is end") f = false } else { println("T2 is waiting") Thread.sleep(1000) } } } } class Obj { @volatile var flag: Boolean = false } object Test { def main(args : Array[String]) { val obj = new Obj() val t1 = new T1(obj) val t2 = new T2(obj) t1.start() t2.start() } } |
\7. 給出一個示例,展示如果方法可被重寫,則尾遞迴優化為非法。
\8. 將allDifferent 方法新增到物件,編譯並檢查位元組碼。@specialized 註解產生了哪些方法?
1 2 3 |
object SpecTest { def allDifferent[@specialized T](x:T,y:T,z:T) = x != y && x!= z && y != z } |
用javap 得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@devswan scala]# javap ../../../target/scala-2.10/classes/SpecTest\$.class Compiled from "spec.scala" public final class SpecTest$ { public static final SpecTest$ MODULE$; public static {}; public <T extends java/lang/Object> boolean allDifferent(T, T, T); 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.BoxedUnit, scala.runtime.BoxedUnit); } |
\9. Range.foreach 方法被註解為 @specialized(Unit)。為什麼? 通過以下命令檢查位元組碼:
1 |
javap -classpath /path/to/scala/lib/scala-library.jar scala.collection.immutable.Range |
並考慮Function1 上的@specialized 註解。點選ScalaDoc 中的Function1.scala連結進行檢視
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可能返回其他型別,但是此方法體根本就不返回,即使設定了也無法獲得返回值
\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
1 |
-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; //Field scala/Predef$.MODULE$:Lscala/Predef$;
3: iload_1
4: iconst_0
5: if_icmple 12
8: iconst_1
9: goto 13
12: iconst_0
13: invokevirtual #23; //Method scala/Predef$.assert:(Z)V
16: iload_1
17: ireturn
......
|