首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >声明Scala案例类的缺点是什么?

声明Scala案例类的缺点是什么?
EN

Stack Overflow用户
提问于 2011-01-11 01:46:28
回答 5查看 22K关注 0票数 107

如果您正在编写使用大量漂亮的、不可变的数据结构的代码,case类似乎是天赐之物,只需一个关键字就可以免费获得以下所有内容:

  • 默认情况下一切都是不可变的
  • 自动定义的Getters
  • 体面的toString()实现
  • 兼容等于()和hashCode()
  • 不适用()方法匹配的伴随对象

但是将不可变的数据结构定义为case类有哪些缺点呢?

它对类或其客户端设置了哪些限制?

在某些情况下,您应该选择非案例类吗?

EN

Stack Overflow用户

发布于 2016-06-09 12:24:59

在某些情况下,您应该选择非案例类吗?

Martin在他的课程Scala中的函数编程原则 (讲座4.6 -模式匹配)中给了我们一个很好的起点,当我们必须在类和案例类之间进行选择时,我们可以使用它。Scala的例子的第7章包含了相同的示例。

比如说,我们想为算术表达式编写一个解释器。为了一开始就保持简单,我们将自己限制在数字和+操作上。此类表达式可以表示为类层次结构,以抽象基类Expr作为根,并以两个子类Number和Sum表示。然后,表达式1+ (3 + 7)将表示为 新和(新数(1),新和(新数(3),新数(7)

代码语言:javascript
运行
复制
abstract class Expr {
  def eval: Int
}

class Number(n: Int) extends Expr {
  def eval: Int = n
}

class Sum(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval + e2.eval
}

此外,添加一个新的Prod类不会导致对现有代码的任何更改:

代码语言:javascript
运行
复制
class Prod(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval * e2.eval
}

相反,添加一个新方法需要修改所有现有的类。

代码语言:javascript
运行
复制
abstract class Expr { 
  def eval: Int 
  def print
} 

class Number(n: Int) extends Expr { 
  def eval: Int = n 
  def print { Console.print(n) }
}

class Sum(e1: Expr, e2: Expr) extends Expr { 
  def eval: Int = e1.eval + e2.eval
  def print { 
   Console.print("(")
   print(e1)
   Console.print("+")
   print(e2)
   Console.print(")")
  }
}

用案例类解决了同样的问题。

代码语言:javascript
运行
复制
abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

添加新方法是一种局部更改。

代码语言:javascript
运行
复制
abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
  }
}

添加一个新的Prod类可能需要更改所有模式匹配。

代码语言:javascript
运行
复制
abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
    case Prod(e1,e2) => e1.eval * e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
    case Prod(e1,e2) => ...
  }
}

视频节目4.6模式匹配的记录

这两种设计都是非常好的,它们之间的选择有时是一个风格的问题,但也有一些标准是重要的。 其中一个条件可能是,是更经常地创建新的表达式子类,还是更频繁地创建新的方法?,因此它是一个考虑系统未来可扩展性和可能的扩展传递的标准。 --如果您所做的主要是创建新的子类,那么实际上面向对象的分解解决方案占据了上风。原因是,只要创建一个新的子类就很容易,而且是一个非常本地的更改,在这个子类中,就像函数解决方案一样,您必须返回,并更改eval方法中的代码,并向其中添加一个新的案例。 另一方面,如果您所做的是创建许多新方法,但是类层次结构本身将保持相对稳定,那么模式匹配实际上是有利的。因为,同样,模式匹配解决方案中的每个新方法都只是一个本地更改,无论您将它放在基类中,还是在类层次结构之外。而一个新的方法,比如面向对象的分解中的显示,需要一个新的增量,每个子类都是这样。所以会有更多的部分,你必须去触摸。 因此,这种扩展性在两个维度中的问题--您可能想要向层次结构中添加新的类,或者您可能想要添加新的方法,或者两者兼而有之--被命名为-- expression

记住:我们必须把这当作一个起点,而不是像唯一的标准。

票数 7
EN
查看全部 5 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4653424

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档