3
  {
      class MyClass(name: String) {} 
      val x = new MyClass("x")
      println(x.name)         // Error name is not a member of MyClass
  }

  {
      abstract class Base

      case class MyClass(name: String) extends Base {}

      var x = new MyClass("x")
      println(x.name)           // name is a member of MyClass
  }

那么,案例类有什么用呢?为什么所有的构造函数参数都变成了变量。

4

4 回答 4

2

name在这两个示例中都是成员,但在您的第一个示例中是私有的,而在您的第二个示例中是公开的。案例类public val默认设置其构造函数参数。

于 2013-08-12T18:46:42.420 回答
2

模式匹配是案例类最重要但不是唯一的应用。另一个重要的一点是,它们根据构造函数参数(也称为产品元素)实现了equalshashCode方法。因此,案例类对于定义用作集合中的元素或映射中的键的数据结构非常有用。反过来,只有当这些元素可见时才有意义。

相比:

class Foo(val i: Int)

val set1 = Set(new Foo(33))
set1.contains(new Foo(33))  // false!!

和:

case class Bar(val i: Int)

val set2 = Set(Bar(33)
set2.contains(Bar(33))      // true!

两个具有相同参数的案例类实例本身是相等的。你可以想象它们代表一些“常数”。这意味着您不应该在其中具有可变状态。

但是,您可以使用第二个参数列表从等式中排除参数:

case class Baz(i: Int)(val n: Long)

Baz(33)(5L) == Baz(33)(6L)  // true!

暗示构造函数参数成为值的另一个有用功能是制作副本。这就是不可变数据改变的方式——你创建一个新的实例,改变了一个特定的值,保留原始值。

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

val p1 = Person("Fuzzi", 33)
val p2 = p1.copy(age = 34)

copy 方法对所有未指定的参数使用默认值,并从构造函数 args 中获取这些值。

于 2013-08-12T19:15:23.553 回答
2

需要明确的是,构造函数参数不用于创建变量,它们用于创建值。

如果您val在第一个示例中指定非案例类:

class MyClass(val name: String) {} 

然后您还将参数转换为公共值,与案例类所做的相同。

Scala-Lang 网站上的示例中,它说:

如果使用模式匹配来分解数据结构,那么定义案例类才有意义。以下对象为我们的 lambda 演算表示定义了一个漂亮的打印机函数:

后跟示例代码:

object TermTest extends Application {   def printTerm(term: Term) {
    term match {
      case Var(n) =>
        print(n)
      case Fun(x, b) =>
        print("^" + x + ".")
        printTerm(b)
      case App(f, v) =>
        Console.print("(")
        printTerm(f)
        print(" ")
        printTerm(v)
        print(")")
    }   }   def isIdentityFun(term: Term): Boolean = term match {
    case Fun(x, Var(y)) if x == y => true
    case _ => false   }   val id = Fun("x", Var("x"))   val t = Fun("x", Fun("y", App(Var("x"), Var("y"))))   printTerm(t)   println   println(isIdentityFun(id))   println(isIdentityFun(t)) }
于 2013-08-12T18:48:21.427 回答
1

由于缺少可用空间而在我的评论中添加一些内容:考虑以下示例案例类:

case class My(x: Int)

如果将其保存到文件并将其传递给scalac -print,则会得到以下扩展代码(我删除了不重要的内容):

case class My extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _; 
<stable> <caseaccessor> <accessor> <paramaccessor> def x(): Int = My.this.x;

注意<caseaccessor>这里。

然后是伴生对象:

      <synthetic> object My extends runtime.AbstractFunction1 with Serializable {
        case <synthetic> def apply(x: Int): My = new My(x);
        case <synthetic> def unapply(x$0: My): Option = if (x$0.==(null))
          scala.this.None
        else
          new Some(scala.Int.box(x$0.x()));
        case <synthetic> <bridge> def apply(v1: Object): Object = My.this.apply(scala.Int.unbox(v1));
//...

注意applyunapply这里。如果您自己查看完整的输出,您将了解有关 scala 如何生成代码的更多信息。

于 2013-08-12T19:10:00.743 回答