没有vars的scala编程

时间:2012-12-13 04:24:07

标签: scala

val和var in scala,我认为这个概念是可以理解的。

我想做类似这样的事情(像java一样):

trait PersonInfo {
  var name: Option[String] = None
  var address: Option[String] = None
  // plus another 30 var, for example 
 }
case class Person() extends PersonInfo
object TestObject {
  def main(args: Array[String]): Unit = {
         val p = new Person()
         p.name = Some("someName")
         p.address = Some("someAddress")
  }
}

所以我可以更改姓名,地址等......

这很好用,但问题是,在我的程序中,我最终将所有内容都视为变量。 据我所知,val在scala中是“首选”。 val如何在此工作 每次更改其中一个参数时,不必重写所有30多个参数的示例类型?

也就是说,我可以

trait PersonInfo {
  val name: Option[String] 
  val address: Option[String] 
  // plus another 30 val, for example 
 }
case class Person(name: Option[String]=None, address: Option[String]=None, ...plus another 30.. ) extends PersonInfo
object TestObject {
  def main(args: Array[String]): Unit = {
         val p = new Person("someName", "someAddress", .....)
         // and if I want to change one thing, the address for example
         val p2 = new Person("someName", "someOtherAddress", .....)
  }
}

这是“正常”的scala做事方式(不能承受22个参数限制)吗? 可以看出,我对这一切都很陌生。


首先是Tony K的基本选项:

def withName(n:String)= Person(n,address)

看起来很有前途,但我有很多类扩展了PersonInfo。 这意味着在每一个我都必须重新实现defs,大量的打字和切割和粘贴, 只是做一些简单的事情。 如果我将特质PersonInfo转换为普通类并将所有defs放入其中,那么 我有一个问题,我怎么能返回一个Person,而不是PersonInfo? 是否有一个聪明的scala事物以某种方式在特质或超类中实现并具有 所有子类真的扩展?

据我所知,当示例非常简单时,scala中的所有工作都很好, 2或3个参数,但是当你有几十个时,它变得非常乏味和不可行。

weirdcanada的PersonContext是我认为类似的,仍在考虑这个。我想如果 我有43个参数需要分解成多个临时类才能用于泵送 将参数转化为人。

复制选项也很有趣,含糊不清但输入的次数要少得多。

来自java我希望scala有一些聪明的技巧。

3 个答案:

答案 0 :(得分:10)

案例类有一个预定义的copy方法,您应该使用它。

case class Person(name: String, age: Int)

val mike = Person("Mike", 42)

val newMike = mike.copy(age = 43)

这是如何工作的? copy只是编译器为您编写的方法之一(除了equalshashCode等)。在这个例子中它是:

def copy(name: String = name, age: Int = age): Person = new Person(name, age)

此方法中的值nameage会影响外部范围中的值。如您所见,提供了默认值,因此您只需指定要更改的值。其他默认为当前实例中的内容。

答案 1 :(得分:5)

scala中存在var的原因是支持可变状态。在某些情况下,可变状态确实是您想要的(例如出于性能或清晰度原因)。

但是,你是正确的,鼓励使用不可变状态背后有很多证据和经验。事情在很多方面都有效(并发性,理性清晰等)。

你的问题的一个答案是为有问题的类提供mutator方法,这些方法实际上不会改变状态,而是返回一个带有修改条目的新对象:

case class Person(val name : String, val address : String) {
   def withName(n : String) = Person(n, address)
   ...
}

这个特定的解决方案确实涉及编码可能很长的参数列表,但仅限于类本身。用户很容易下载:

val p = Person("Joe", "N St")
val p2 = p.withName("Sam")
...

如果您考虑原因,您想要改变状态,那么事情会变得更加清晰。如果您正在从数据库中读取数据,那么您可能有很多理由来改变对象:

  • 数据库本身已更改,您希望自动刷新内存中对象的状态
  • 您想要对数据库本身进行更新
  • 您希望传递一个对象,并通过遍布各处的方法进行变异

在第一种情况下,不可变状态很容易:

val updatedObj = oldObj.refresh

第二种方法要复杂得多,并且有很多方法可以处理它(包括脏字段跟踪的可变状态)。查看像Squery这样的库是值得的,在那里你可以用漂亮的DSL编写东西(参见http://squeryl.org/inserts-updates-delete.html)并避免完全使用直接对象变异。

最后一个是您通常希望避免出于复杂性的原因。这样的事情难以并行化,难以推理,并导致各种错误,其中一个类引用另一个类,但不保证它的稳定性。这种用法是我们所讨论的形式的不可变状态的尖叫。

答案 2 :(得分:0)

Scala采用了许多功能编程范例,其中之一是关注使用具有不可变状态的对象。这意味着要远离类中的getter和setter,而是选择执行@Tony K.上面提到的内容:当你需要更改内部对象的“状态”时,定义一个将返回一个新的{{ 1}}对象。

尝试使用不可变对象可能是首选 Scala方式。

关于22参数问题,您可以创建一个传递给Person的构造函数的上下文类:

Person

如果您发现自己经常更改地址而又不想通过case class PersonContext(all: String, of: String, your: String, parameters: Int) class Person(context: PersonContext) extends PersonInfo { ... } rigamarole,则可以定义方法:

PersonContext

您可以更进一步,并在def addressChanger(person: Person, address: String): Person = { val contextWithNewAddress = ... Person(contextWithNewAddress) } 上定义方法:

Person

在您的代码中,您只需要记住,当您更新对象时,您经常会获得新对象。一旦你习惯了这个概念,它就变得非常自然了。