简体   繁体   中英

How val is initialised in two objects inheriting the same trait in scala?

I think my question is related but not the same as this one here .

Let define my first 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)
}

Now I have a trait

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
}
}

I intend to use this trait to mix in with any classes where I want to collect some notes and only print out the note when there is an exception. Otherwise, I maybe just save it to a log file somewhere.

I want the notepad to be created once and reused for each of the object. In fact, I don't mind if they share the same notepad. Therefore, I chose to use 'val' in my trait.

As an example, I then create a 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
    })
}

And finally I try to create two objects as follow:

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)

I'm really confused here. Obviously an exception occurred. But the secondObject was created just fine? But the logging message get printed out on stdout with the error 'null'.

I think my question is whether my first and second objects are actually using the same notepad or separate? How are the initialisation and the scope of notepad defined here? Is there something wrong with the way I use 'Try'?

This is caused of anonymous function with explicitly return :

   (x: String) => {
        notepad.add(x)
        val result = this.copy(myField = this.myField + " addNewField " + x)
        notepad.add(result.myField)
        return result
    }

In Scala , when explicitly declare return in anonymous function , it will throw NonLocalReturnControl , this will skip the later code block execute, since you have catched the Throwable , so it also will go to your catch code block .

So maybe you can remove return directly to solve this issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM