Saurabh
Saurabh

Reputation: 73589

How to set a local variable in other method

Is it possible to set a variable which has scope in a method from it's child class in scala?

Why I need is explained in following code snippet:

abstract class SuperTask {
  def doWork():Unit = {
     var taskData:Option[TaskData] = None
     try {
        _actualWork() 
     }
     catch {
      //  catch exception and do reporting using taskData variable
     } 
  }

  protected def _actualWork(): Unit
}

class FunTask extends SuperTask {
    override def _actualWork(): Unit = {
       //This will have data for taskData
       //PROBLEM is how to set taskData of parent class from here so that if this throws exception, I can do reporting from catch in doWork method.
    }
}

I want to set value of taskData variable, so that if _actualWork fails, I can have proper reporting, retries, etc which can be common for all child classes of SuperTask class.

I can not define taskData variable at SuperTask class level as it then will be shared and fail in case of concurrency.

Upvotes: 0

Views: 88

Answers (2)

Andrey Tyukin
Andrey Tyukin

Reputation: 44908

Just pass a closure which knows how to set taskData to _actualWork:

type TaskData = String

abstract class SuperTask {
  def doWork():Unit = {
    var taskData:Option[TaskData] = None
    try {
       _actualWork(td => taskData = td) 
    } catch {
      case _: Throwable => println("last task data: " + taskData)
    } 
  }

  protected def _actualWork(setTaskData: Option[TaskData] => Unit): Unit
}

object FunTask extends SuperTask {
  override def _actualWork(setTaskData: Option[TaskData] => Unit): Unit = {
    // No problem to set taskData of parent class from here:
    setTaskData(Some("Hello, world!"))
    throw new Error("error")
  }
}

If you now invoke doWork on FunTask, which sets the taskData and throws an error, you get the following output:

FunTask.doWork() 
// output: last task data: Some(Hello, world!)

Note that this is merely a really cheap variant of the "Observer"-pattern, where the state of your registered "observer" consists of a single local variable, and the "notifications" consist of invocations of the closure setTaskData. You could just as well pass a whole bunch of proper "observers" into the _actualWork method, so that they can monitor what's going on inside _actualWork.

Upvotes: 1

dyrkin
dyrkin

Reputation: 544

This regarding my last comment below your message:

Split _actualWork into two functions? 1st one is Option[TaskData] and the 2nd one is _actualWork(taskData: Option[TaskData]).

abstract class SuperTask {
  def doWork():Unit = {
     val taskData:Option[TaskData] = getData
     try {
        _actualWork(taskData) 
     }
     catch {
      //  catch exception and do reporting using taskData variable
     } 
  }

  protected def getData(): Option[TaskData]
  protected def _actualWork(taskData: Option[TaskData]): Unit
}

class FunTask extends SuperTask {
    override def getData(): Option[TaskData] = Some(data)
    override def _actualWork(taskData: Option[TaskData]): Unit = {
       //do some work with taskData
    }
}

Upvotes: 0

Related Questions