如何在继承 Scala 中相同特征的两个对象中初始化 val?
How val is initialised in two objects inheriting the same trait in scala?
我认为我的问题与这个问题相关但不同here。
让我们定义我的第一个class
case class NoteTaker() {
private var note: Seq[String] = Seq("\n")
override def toString: String = this.note.mkString("\n")
def add(newNote: String): Unit = note ++= Seq(newNote)
}
现在我有一个特质
trait SilentLogger {
import scala.util.{ Failure, Success }
val notepad = NoteTaker()
def tryAndLog[X, Y](x: X, f: X => Y): Y = {
notepad.add("Run with this input: " + x.toString)
(try {
println("Before: " + notepad.toString)
val result = f(x)
println("After: " + notepad.toString)
notepad.add("Get result:-------------------------------\n" + result.toString)
println(notepad.toString)
Success(result)
} catch {
case e: Throwable => {
println(
"Exception occurs:" + "\n" +
notepad.toString + "\n" +
e.getMessage + "\n" +
e.getStackTrace.mkString("\n")
)
Failure(e)
}}).get
}
}
我打算使用这个特性与任何 classes 混合在一起,我想收集一些笔记并且只在有异常时打印出笔记。否则,我可能只是将它保存到某处的日志文件中。
我希望记事本创建一次并为每个对象重复使用。事实上,我不介意他们共用同一个记事本。因此,我选择在我的特征中使用 'val' 。
例如,我创建了一个 class
case class MyClass (myField : String) extends SilentLogger {
def makeAnother : MyClass = tryAndLog("makeAnother",(x: String) => {
notepad.add(x)
val result = this.copy(myField = this.myField + " addNewField " + x)
notepad.add(result.myField)
return result
})
}
最后我尝试创建两个对象如下:
scala> val firstObject = MyClass("firstObject")
firstObject: MyClass = MyClass(firstObject)
scala> val secondObject = firstObject.makeAnother
Before:
Run with this input: makeAnother
Exception occurs:
Run with this input: makeAnother
makeAnother
firstObject addNewField makeAnother
null
secondObject: MyClass = MyClass(firstObject addNewField makeAnother)
我真的很困惑。显然发生了异常。但是 secondObject 创建得很好吗?但是日志消息在 stdout 上打印出来,错误为 'null'。
我想我的问题是我的第一个和第二个对象实际上是在使用同一个记事本还是分开的?这里记事本的初始化和范围是如何定义的?我使用的方式有问题吗'Try'?
这是由显式 return
:
的匿名函数引起的
(x: String) => {
notepad.add(x)
val result = this.copy(myField = this.myField + " addNewField " + x)
notepad.add(result.myField)
return result
}
在Scala中,当在匿名函数中显式声明return
时,会抛出NonLocalReturnControl,这个将跳过后面的 代码块 执行,因为您已经捕获了 Throwable
,所以它也会转到您的 catch code block
。
所以也许你可以直接删除 return
来解决这个问题。
我认为我的问题与这个问题相关但不同here。
让我们定义我的第一个class
case class NoteTaker() {
private var note: Seq[String] = Seq("\n")
override def toString: String = this.note.mkString("\n")
def add(newNote: String): Unit = note ++= Seq(newNote)
}
现在我有一个特质
trait SilentLogger {
import scala.util.{ Failure, Success }
val notepad = NoteTaker()
def tryAndLog[X, Y](x: X, f: X => Y): Y = {
notepad.add("Run with this input: " + x.toString)
(try {
println("Before: " + notepad.toString)
val result = f(x)
println("After: " + notepad.toString)
notepad.add("Get result:-------------------------------\n" + result.toString)
println(notepad.toString)
Success(result)
} catch {
case e: Throwable => {
println(
"Exception occurs:" + "\n" +
notepad.toString + "\n" +
e.getMessage + "\n" +
e.getStackTrace.mkString("\n")
)
Failure(e)
}}).get
}
}
我打算使用这个特性与任何 classes 混合在一起,我想收集一些笔记并且只在有异常时打印出笔记。否则,我可能只是将它保存到某处的日志文件中。
我希望记事本创建一次并为每个对象重复使用。事实上,我不介意他们共用同一个记事本。因此,我选择在我的特征中使用 'val' 。
例如,我创建了一个 class
case class MyClass (myField : String) extends SilentLogger {
def makeAnother : MyClass = tryAndLog("makeAnother",(x: String) => {
notepad.add(x)
val result = this.copy(myField = this.myField + " addNewField " + x)
notepad.add(result.myField)
return result
})
}
最后我尝试创建两个对象如下:
scala> val firstObject = MyClass("firstObject")
firstObject: MyClass = MyClass(firstObject)
scala> val secondObject = firstObject.makeAnother
Before:
Run with this input: makeAnother
Exception occurs:
Run with this input: makeAnother
makeAnother
firstObject addNewField makeAnother
null
secondObject: MyClass = MyClass(firstObject addNewField makeAnother)
我真的很困惑。显然发生了异常。但是 secondObject 创建得很好吗?但是日志消息在 stdout 上打印出来,错误为 'null'。
我想我的问题是我的第一个和第二个对象实际上是在使用同一个记事本还是分开的?这里记事本的初始化和范围是如何定义的?我使用的方式有问题吗'Try'?
这是由显式 return
:
(x: String) => {
notepad.add(x)
val result = this.copy(myField = this.myField + " addNewField " + x)
notepad.add(result.myField)
return result
}
在Scala中,当在匿名函数中显式声明return
时,会抛出NonLocalReturnControl,这个将跳过后面的 代码块 执行,因为您已经捕获了 Throwable
,所以它也会转到您的 catch code block
。
所以也许你可以直接删除 return
来解决这个问题。