1. 程式人生 > >Scala練習(十五)

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
.println(t.sum(1, 3, 4)); } }

\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
......