【scala原理系列】scala console println源码分析

本文详细解析了Scala中的consoleprintln源码,介绍了Console对象的工作原理,使用DynamicVariable管理输出流、错误流和输入流,以及提供的一系列操作流和进行输入输出的方法,包括setOutDirect、setErrDirect、setInDirect等以及withOut、withErr和withIn等辅助函数的用法示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scala console println源码分析

原理

这段源码定义了一个名为Console的对象,它提供了在终端上打印和读取值的功能。

在源码中,Console对象使用了DynamicVariable类来保存默认的输出流、错误流和输入流。DynamicVariable是Scala标准库提供的一种机制,用于创建线程本地变量。通过使用DynamicVariableConsole对象可以为不同的线程设置独立的输出流、错误流和输入流,而不会相互干扰。

Console对象提供了一系列方法来操作默认的输出流、错误流和输入流。例如,setOutDirectsetErrDirectsetInDirect方法用于直接设置默认的输出流、错误流和输入流。withOutwithErrwithIn方法则允许在执行指定的代码块期间临时更改默认的输出流、错误流和输入流,以便进行特定的操作。

除了操作流之外,Console对象还提供了一些打印和读取值的方法。例如,print方法用于将对象以字符串形式打印到默认的输出流,println方法在默认输出流上打印一个换行字符,printf方法用于格式化打印字符串。另外,readLine方法用于从默认输入流中读取一行文本,readBooleanreadBytereadChar等方法用于从默认输入流中读取不同类型的值。

通过使用Console对象,开发人员可以方便地在终端上进行输入和输出操作,并且可以根据需要灵活地切换默认的输出流、错误流和输入流。这对于开发命令行应用程序或与用户进行交互的程序非常有用。

方法总结

方法描述
setOutDirect(out)设置默认输出流为指定的PrintStream对象。
setErrDirect(err)设置默认错误流为指定的PrintStream对象。
setInDirect(in)设置默认输入流为指定的BufferedReader对象。
out获取当前默认的输出流。
err获取当前默认的错误流。
in获取当前默认的输入流。
withOutT(thunk)在执行给定的代码块期间,将默认输出流更改为指定的PrintStream对象,并返回代码块的结果。
withOutT(thunk)在执行给定的代码块期间,将默认输出流更改为通过给定的OutputStream对象创建的PrintStream对象,并返回代码块的结果。
withErrT(thunk)在执行给定的代码块期间,将默认错误流更改为指定的PrintStream对象,并返回代码块的结果。
withErrT(thunk)在执行给定的代码块期间,将默认错误流更改为通过给定的OutputStream对象创建的PrintStream对象,并返回代码块的结果。
withInT(thunk)在执行给定的代码块期间,将默认输入流更改为指定的Reader对象,并返回代码块的结果。
withInT(thunk)在执行给定的代码块期间,将默认输入流更改为通过给定的InputStream对象创建的Reader对象,并返回代码块的结果。
print(obj)将指定的对象使用其toString方法打印到默认输出流。如果对象为null,则打印字符串"null"。
flush()刷新输出流。在需要将部分输出(即未以换行符结束的输出)显示在终端上时,需要调用此方法。
println()在默认输出流中打印一个换行符。
println(x)在默认输出流中打印一个对象,后跟一个换行符。
printf(text, args)根据指定的格式字符串(类似于C语言中的printf函数),将参数作为格式化字符串打印到默认输出流中。格式字符串的解释可以参考java.util.Formatter的文档。参数args是要填充格式字符串的值。如果格式字符串或参数存在问题,则抛出IllegalArgumentException异常。

示例

// 设置默认输出流为指定的PrintStream对象
Console.setOutDirect(System.out)

// 设置默认错误流为指定的PrintStream对象
Console.setErrDirect(System.err)

// 设置默认输入流为指定的BufferedReader对象
val reader = new BufferedReader(new InputStreamReader(System.in))
Console.setInDirect(reader)

// 获取当前默认的输出流
val outStream = Console.out

// 获取当前默认的错误流
val errStream = Console.err

// 获取当前默认的输入流
val inputStream = Console.in

// 在执行给定的代码块期间,将默认输出流更改为指定的PrintStream对象,并返回代码块的结果
val result1 = Console.withOut(System.out) {
  println("This is printed to the specified output stream")
  "Result of the code block"
}

// 在执行给定的代码块期间,将默认输出流更改为通过给定的OutputStream对象创建的PrintStream对象,并返回代码块的结果
val result2 = Console.withOut(System.out) {
  println("This is printed to the specified output stream")
  "Result of the code block"
}

// 在执行给定的代码块期间,将默认错误流更改为指定的PrintStream对象,并返回代码块的结果
val result3 = Console.withErr(System.err) {
  println("This is printed to the specified error stream")
  "Result of the code block"
}

// 在执行给定的代码块期间,将默认错误流更改为通过给定的OutputStream对象创建的PrintStream对象,并返回代码块的结果
val result4 = Console.withErr(System.err) {
  println("This is printed to the specified error stream")
  "Result of the code block"
}

// 在执行给定的代码块期间,将默认输入流更改为指定的Reader对象,并返回代码块的结果
val result5 = Console.withIn(new FileReader("file.txt")) {
  val line = Console.in.readLine()
  println(s"Read from the specified input stream: $line")
  "Result of the code block"
}

// 在执行给定的代码块期间,将默认输入流更改为通过给定的InputStream对象创建的Reader对象,并返回代码块的结果
val result6 = Console.withIn(new FileInputStream("file.txt")) {
  val line = Console.in.readLine()
  println(s"Read from the specified input stream: $line")
  "Result of the code block"
}

// 将指定的对象使用其toString方法打印到默认输出流
Console.print("Hello, World!")

// 刷新输出流
Console.flush()

// 在默认输出流中打印一个换行符
Console.println()

// 在默认输出流中打印一个对象,后跟一个换行符
Console.println("Hello, World!")

// 根据指定的格式字符串,将参数作为格式化字符串打印到默认输出流中
Console.printf("Name: %s, Age: %d", "John Doe", 25)

注意:以上示例代码仅用于演示每个方法的使用方式,并非可运行代码。

中文源码

/**
 * 实现在终端上打印Scala值以及读取特定值的功能。
 * 还定义了用于在ANSI终端上标记文本的常量。
 *
 * 作者:Matthias Zenger
 * 版本:1.0,03/09/2003
 */
object Console extends DeprecatedConsole with AnsiColor {
  private val outVar = new DynamicVariable[PrintStream](java.lang.System.out)
  private val errVar = new DynamicVariable[PrintStream](java.lang.System.err)
  private val inVar  = new DynamicVariable[BufferedReader](
    new BufferedReader(new InputStreamReader(java.lang.System.in)))

  protected def setOutDirect(out: PrintStream): Unit  = outVar.value = out
  protected def setErrDirect(err: PrintStream): Unit  = errVar.value = err
  protected def setInDirect(in: BufferedReader): Unit = inVar.value = in

  /** 默认的输出流,可以通过`setOut`进行覆盖 */
  def out = outVar.value
  /** 默认的错误流,可以通过`setErr`进行覆盖 */
  def err = errVar.value
  /** 默认的输入流,可以通过`setIn`进行覆盖 */
  def in = inVar.value

  /** 在执行一个thunk期间设置默认的输出流。
   *
   *  @example {{{
   *  withOut(Console.err) { println("This goes to default _error_") }
   *  }}}
   *
   *  @param out 新的输出流
   *  @param thunk 使用新的输出流执行的代码块
   *  @return `thunk`的结果
   *  @see `withOut[T](out:OutputStream)(thunk: => T)`
   */
  def withOut[T](out: PrintStream)(thunk: =>T): T =
    outVar.withValue(out)(thunk)

  /** 在执行一个thunk期间设置默认的输出流。
   *
   *  @param out 新的输出流
   *  @param thunk 使用新的输出流执行的代码块
   *  @return `thunk`的结果
   *  @see `withOut[T](out:PrintStream)(thunk: => T)`
   */
  def withOut[T](out: OutputStream)(thunk: =>T): T =
    withOut(new PrintStream(out))(thunk)

  /** 在执行一个thunk期间设置默认的错误流。
   *  @example {{{
   *  withErr(Console.out) { println("This goes to default _out_") }
   *  }}}
   *
   *  @param err 新的错误流
   *  @param thunk 使用新的错误流执行的代码块
   *  @return `thunk`的结果
   *  @see `withErr[T](err:OutputStream)(thunk: =>T)`
   */
  def withErr[T](err: PrintStream)(thunk: =>T): T =
    errVar.withValue(err)(thunk)

  /** 在执行一个thunk期间设置默认的错误流。
   *
   *  @param err 新的错误流
   *  @param thunk 使用新的错误流执行的代码块
   *  @return `thunk`的结果
   *  @see `withErr[T](err:PrintStream)(thunk: =>T)`
   */
  def withErr[T](err: OutputStream)(thunk: =>T): T =
    withErr(new PrintStream(err))(thunk)

  /** 在执行一个thunk期间设置默认的输入流。
   *
   *  @example {{{
   *  val someFile:Reader = openFile("file.txt")
   *  withIn(someFile) {
   *    // 从file.txt中读取一行,而不是默认的输入流
   *    println(readLine)
   *  }
   *  }}}
   *
   *  @param reader 使用新的输入流执行的代码块
   *
   * @return `thunk`的结果
   * @see `withIn[T](in:InputStream)(thunk: =>T)`
   */
  def withIn[T](reader: Reader)(thunk: =>T): T =
    inVar.withValue(new BufferedReader(reader))(thunk)

​```scala
/** 设置默认的输入流,在执行一个thunk期间使用该输入流。
 *
 * @param in 新的输入流。
 * @param thunk 使用新的输入流执行的代码块。
 * @return `thunk`的结果。
 * @see `withIn[T](reader:Reader)(thunk: =>T)`
 */
def withIn[T](in: InputStream)(thunk: =>T): T =
  withIn(new InputStreamReader(in))(thunk)

/** 使用对象的`toString`方法将对象打印到`out`流中。
 *
 * @param obj 要打印的对象,可以为null。
 */
def print(obj: Any) {
  out.print(if (null == obj) "null" else obj.toString())
}

/** 刷新输出流。当需要在终端上显示部分输出(即未以换行符结尾的输出)时,需要使用此函数。
 */
def flush() { out.flush() }

/** 在默认输出流中打印一个换行字符。
 */
def println() { out.println() }

/** 将对象打印到默认输出流中,后面跟着一个换行字符。
 *
 * @param x 要打印的对象。
 */
def println(x: Any) { out.println(x) }

/** 根据字符串模式(类似于C语言中的printf函数),将参数作为格式化字符串打印到默认输出流中。
 *
 * 格式化模式的解释请参见`java.util.Formatter`。
 *
 * @param text 格式化模式的字符串。
 * @param args 实例化模式的参数。
 * @throws java.lang.IllegalArgumentException 如果格式字符串或参数有问题。
 */
def printf(text: String, args: Any*) { out.print(text format (args : _*)) }
}

private[scala] abstract class DeprecatedConsole {
self: Console.type =>

    /** 仅供内部使用。 */
    protected def setOutDirect(out: PrintStream): Unit
    protected def setErrDirect(err: PrintStream): Unit
    protected def setInDirect(in: BufferedReader): Unit

    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readBoolean(): Boolean                     = StdIn.readBoolean()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readByte(): Byte                           = StdIn.readByte()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readChar(): Char                           = StdIn.readChar()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readDouble(): Double                       = StdIn.readDouble()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readFloat(): Float                         = StdIn.readFloat()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readInt(): Int                             = StdIn.readInt()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readLine(): String                         = StdIn.readLine()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readLine(text: String, args: Any*): String = StdIn.readLine(text, args: _*)
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readLong(): Long                           = StdIn.readLong()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readShort(): Short                         = StdIn.readShort()
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readf(format: String): List[Any]           = StdIn.readf(format)
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readf1(format: String): Any                = StdIn.readf1(format)
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readf2(format: String): (Any, Any)         = StdIn.readf2(format)
    @deprecated("请使用scala.io.StdIn中的方法", "2.11.0") def readf3(format: String): (Any, Any, Any)    = StdIn.readf3(format)

    /** 设置默认的输出流。
     *
     * @param out 新的输出流。
     */
    @deprecated("请使用withOut", "2.11.0") def setOut(out: PrintStream): Unit = setOutDirect(out)

    /** 设置默认的输出流。
     *
     * @param out 新的输出流。
     */
    @deprecated("请使用withOut", "2.11.0") def setOut(out: OutputStream): Unit = setOutDirect(new PrintStream(out))

    /** 设置默认的错误流。
     *
     * @param err 新的错误流。
     */
    @deprecated("请使用withErr", "2.11.0") def setErr(err: PrintStream): Unit = setErrDirect(err)

    /** 设置默认的错误流。
     *
     * @param err 新的错误流。
     */
    @deprecated("请使用withErr", "2.11.0") def setErr(err: OutputStream): Unit = setErrDirect(new PrintStream(err))

    /** 设置默认的输入流。
     *
     * @param reader 指定新的输入流。
     */
    @deprecated("请使用withIn", "2.11.0") def setIn(reader: Reader): Unit = setInDirect(new BufferedReader(reader))

    /** 设置默认的输入流。
     *
     * @param in 新的输入流。
     */
    @deprecated("请使用withIn", "2.11.0") def setIn(in: InputStream): Unit = setInDirect(new BufferedReader(new InputStreamReader(in)))
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值