Scala 找出一种将var实现转换为val的方法

Scala 找出一种将var实现转换为val的方法,scala,functional-programming,var,Scala,Functional Programming,Var,我正在使用Java库中的以下接口: // This is a Java class from a library I'm using public interface Listener { void receiveConfigInfo(final String configInfo); } 我在Scala中将其扩展如下: //MyClass needs to extends the Listener interface final class MyClass extends Listene

我正在使用Java库中的以下接口:

// This is a Java class from a library I'm using
public interface Listener {
  void receiveConfigInfo(final String configInfo);
}
我在Scala中将其扩展如下:

//MyClass needs to extends the Listener interface
final class MyClass extends Listener {
  private var config = Option.empty

  // Implement the the Listener interface
  override def receiveConfigInfo(configInfo: String): Unit = {
    try {
      config  = decode[Map[String, String]](configInfo) match {
        case Right(config) => Some(config)
        case Left(_) => None
      }
    } catch {
      case _: Throwable => nacosConfig = None
    }
  }

  override def getConfig():Option[Map[String, String]] = nacosConfig
}
receiveConfigInfo
将在相关时自动调用

getConfig
返回配置的最新值

有没有办法将
config
转换成
val
,而不是可变的
var
?我无法更改
receiveConfigInfo
的签名,因为它需要尊重父类的签名


目标是无论何时调用
getConfig
,我都应该获得最新的配置值。但是,我当前的实现有一个
var
,这不好,有没有办法更改此代码使其变为val,或者如果可能的话,还有其他方法?

鉴于您正在实现的Java接口似乎需要一个实现来接受任意多个新的
configInfo
,我建议最诚实的做法是保留var,但将其封装为只能通过
receiveConfigInfo
进行更改

所以可能是

object MyClass {
  private[MyClass] Config {
    private var underlying: Map[String, String] = Map.empty

    override def get(key: String): Option[String] = 
      synchronized { underlying.get(key) }

    override def toMap: Map[String, String] =
      synchronized { underlying }

    override def update(configInfo: String): Unit =
      synchronized {
        // I'm assuming that decode is somewhere statically accessible
        // I'm also assuming that a function returning an `Either` won't throw non-fatal exceptions... if it can return a Left, a Right, or throw, then:
        //   Try { decode[...](configInfo) }.flatMap(_.left.map(msg => new RuntimeException(msg)).toTry)
        val decoded =
          decode[Map[String, String]](configInfo)
            .left
            .map(_ => new RuntimeException("decode failed")
            .toTry
        decoded.foreach { newConfig => underlying = newConfig }
        decoded.recoverWith { _ =>
          underlying = Map.empty
          decoded
        }
      }
  }
}

class MyClass extends Listener {
  private val config = new MyClass.Config

  override def receiveConfigInfo(configInfo: String): Unit = config.update(configInfo)
}
有了这样一个解决方案,配置对于
MyClass
中不调用
update
的任何东西都是不可变的(希望这与分配给
var
相比足够明显)


如果出现错误的
configInfo
时,我将保留取消设置配置的行为;在这种情况下,保持
configInfo
不变可能更有意义,这将使在实践中更难无意中更改配置,除非
MyClass
的客户端调用
receiveConfigInfo
使用有效的配置。

鉴于您正在实现的Java接口似乎需要一个实现来接受任意多个新的
configInfo
,我建议最诚实的做法是保留var,但将其封装为只能通过
receiveConfigInfo
更改

所以可能是

object MyClass {
  private[MyClass] Config {
    private var underlying: Map[String, String] = Map.empty

    override def get(key: String): Option[String] = 
      synchronized { underlying.get(key) }

    override def toMap: Map[String, String] =
      synchronized { underlying }

    override def update(configInfo: String): Unit =
      synchronized {
        // I'm assuming that decode is somewhere statically accessible
        // I'm also assuming that a function returning an `Either` won't throw non-fatal exceptions... if it can return a Left, a Right, or throw, then:
        //   Try { decode[...](configInfo) }.flatMap(_.left.map(msg => new RuntimeException(msg)).toTry)
        val decoded =
          decode[Map[String, String]](configInfo)
            .left
            .map(_ => new RuntimeException("decode failed")
            .toTry
        decoded.foreach { newConfig => underlying = newConfig }
        decoded.recoverWith { _ =>
          underlying = Map.empty
          decoded
        }
      }
  }
}

class MyClass extends Listener {
  private val config = new MyClass.Config

  override def receiveConfigInfo(configInfo: String): Unit = config.update(configInfo)
}
有了这样一个解决方案,配置对于
MyClass
中不调用
update
的任何东西都是不可变的(希望这与分配给
var
相比足够明显)


如果出现错误的
configInfo
时,我将保留取消设置配置的行为;在这种情况下,保持
configInfo
不变可能更有意义,这将使在实践中更难无意中更改配置,除非
MyClass
的客户端调用
receiveConfigInfo
有了有效的配置。

如果你真的想使用
val
,你可以使用中的一个可变结构。但是,它会更迂回,可能容易出错,并且在幕后实际使用
var
,因此你也可以直接使用
var
(你的代码已经是这样)

//在类结构中
val config=collection.mutable.IndexedSeq[Option[Map[String,String]](无)
...
//接收配置信息
配置(0)=解码。。。
//在getConfig中
配置(0)

如果您真的想使用
val
,您可以使用中的一种可变结构。但是,它会更迂回,可能会出错,并且在幕后实际使用
var
,因此您最好直接使用
var
(您的代码已经是这样)

//在类结构中
val config=collection.mutable.IndexedSeq[Option[Map[String,String]](无)
...
//接收配置信息
配置(0)=解码。。。
//在getConfig中
配置(0)

没有(简单的)方法删除
var
。好吧,你可以使用
cats effect
中的[Deferred](),但这会改变你使用代码的方式。我宁愿将重点放在删除
try
一个和
None[Map[String,String]]
不是有效语法,也不是必需的。该类需要实现接口not extend。@RajaShekar该示例不幸地将Java和Scala混合在一起,但在Scala中,您可以使用
extend
进行任何操作,包括Java接口(只要它们是您扩展的第一件事,关键字
with
必须用于以下特征和接口)。旁注:不要捕获可丢弃的
s,我建议改用
非致命的
提取器(此答案中的更多详细信息:)。没有(简单)删除
var
的方法。您可以使用
cats effect
中的[Deferred](),但这将大大改变您使用代码的方式。我更愿意将重点放在删除
try
一个
None[Map[String,String]]
不是有效语法,也不是必需的。该类需要实现接口not extend。@RajaShekar该示例不幸地将Java和Scala混合在一起,但在Scala中,您可以使用
extend
进行任何操作,包括Java接口(只要它们是您扩展的第一件事,关键字
with
必须用于以下特征和接口)。旁注:不要捕获可丢弃的
s,我建议改用
非致命的
提取器进行编译(此答案中有更多详细信息:)。