System.out
がConsole.outVar
にキャッシュされるのを知らなかったという話です
print
とprintln
の両方について当てはまるので以下はprint
の例だけを書きます。
print
が処理される流れ
まず前提としてprint
は暗黙にimport Predef._
されてるPredef.print
です。
Predef.scala
object Predef extends LowPriorityImplicits {
def print(x: Any) = Console.print(x)
Console.scala
object Console {
def print(obj: Any) {
out.print(if (null == obj) "null" else obj.toString())
}
def out = outVar.value
private val outVar = new DynamicVariable[PrintStream](java.lang.System.out)
順に辿っていくと、Console.outVar
がSystem.out
の値で初期化されていることがわかります。
Console
はobjectなので遅延評価(最初にメンバーへのアクセスがあった時に評価)されます
System.out
はキャッシュされて、その後のSystem.setOut
は効果を及ぼしません。
Console.setOut
で強制的にConsole.outVar.value
を上書きすることもできますが、2.11から非推奨になっていますので、Console.withOut
を使うのがいいようです。
Console.scala
object Console {
def withOut[T](out: PrintStream)(thunk: =>T): T =
outVar.withValue(out)(thunk)
Test.scala
"Predef.print" should {
"print hello" in {
val outStream = new ByteArrayOutputStream
val out = new PrintStream(new BufferedOutputStream(outStream), true, "utf-8")
Console.withOut(out) {
print("hello")
out.flush()
outStream.toString("utf-8") mustEqual "hello"
}
}
}