代码之家  ›  专栏  ›  技术社区  ›  Teja Kantamneni

Scala的case类和class有什么区别?

  •  398
  • Teja Kantamneni  · 技术社区  · 16 年前

    我在谷歌上搜索,找出了 case class 和一个 class . 每个人都提到,当您想在类上进行模式匹配时,请使用用例类。否则,使用类,并提及一些额外的特权,如等于和哈希代码重写。但是,这些是使用case类而不是class的唯一原因吗?

    我想Scala的这个特性应该有一些非常重要的原因。有什么解释,或者有什么资源可以从中了解有关Scala case类的更多信息?

    14 回复  |  直到 9 年前
        1
  •  418
  •   Sraw    6 年前

    案例类可以看作 包含应完全依赖于其构造函数参数的对象的普通且不可变的数据 .

    这个功能概念允许我们

    • 使用紧凑的初始化语法( Node(1, Leaf(2), None)) )

    结合继承,case类用于模拟 algebraic datatypes

    如果一个对象在内部执行有状态的计算或者表现出其他类型的复杂行为,那么它应该是一个普通的类。

        2
  •  170
  •   Daniel C. Sobral    9 年前

    algebraic data types .

    sealed abstract class Tree
    case class Node(left: Tree, right: Tree) extends Tree
    case class Leaf[A](value: A) extends Tree
    case object EmptyLeaf extends Tree
    

    使我们能够做到以下几点:

    // DSL-like assignment:
    val treeA = Node(EmptyLeaf, Leaf(5))
    val treeB = Node(Node(Leaf(2), Leaf(3)), Leaf(5))
    
    // On Scala 2.8, modification through cloning:
    val treeC = treeA.copy(left = treeB.left)
    
    // Pretty printing:
    println("Tree A: "+treeA)
    println("Tree B: "+treeB)
    println("Tree C: "+treeC)
    
    // Comparison:
    println("Tree A == Tree B: %s" format (treeA == treeB).toString)
    println("Tree B == Tree C: %s" format (treeB == treeC).toString)
    
    // Pattern matching:
    treeA match {
      case Node(EmptyLeaf, right) => println("Can be reduced to "+right)
      case Node(left, EmptyLeaf) => println("Can be reduced to "+left)
      case _ => println(treeA+" cannot be reduced")
    }
    
    // Pattern matches can be safely done, because the compiler warns about
    // non-exaustive matches:
    def checkTree(t: Tree) = t match {
      case Node(EmptyLeaf, Node(left, right)) =>
      // case Node(EmptyLeaf, Leaf(el)) =>
      case Node(Node(left, right), EmptyLeaf) =>
      case Node(Leaf(el), EmptyLeaf) =>
      case Node(Node(l1, r1), Node(l2, r2)) =>
      case Node(Leaf(e1), Leaf(e2)) =>
      case Node(Node(left, right), Leaf(el)) =>
      case Node(Leaf(el), Node(left, right)) =>
      // case Node(EmptyLeaf, EmptyLeaf) =>
      case Leaf(el) =>
      case EmptyLeaf =>
    }
    

    请注意,树用相同的语法构造和解构(通过模式匹配),这也正是它们的打印方式(减去空格)。

        3
  •  76
  •   sepp2k    16 年前
    • Case类自动定义hashcode和equals

    (除了最后一个你已经提到了)。

    这是与普通课程的唯一区别。

        4
  •  31
  •   Jean-Philippe Pellet    15 年前

    Product 从而继承这些方法:

    def productElement(n: Int): Any
    def productArity: Int
    def productIterator: Iterator[Any]
    

    productArity 返回类参数的数目, productElement(i) 返回 参数,以及 productIterator

        5
  •  29
  •   Shelby Moore III    12 年前

    没有人提到案例类 val 构造函数参数这也是常规类的默认值 I think is an inconsistency ".

    注意,您可以通过在每个构造函数参数前面加上 var 对于案例类。然而,使case类可变会导致 equals hashCode 方法是时变的。[1]

    sepp2k公司 前面已经提到案例类自动生成 等于 方法。

    也没有人提到case类会自动创建一个同伴 object 与类同名,其中包含 apply unapply 方法。这个 应用 new . 这个 不悦 提取器方法支持其他人提到的模式匹配。

    match - case

    [1] Case Classes Are Cool

    Case Classes and Extractors, pg 15 .

        6
  •  17
  •   PaÅ­lo Ebermann    7 年前

    当构造一个case类时,Scala提供了以下内容。

    • 它创建一个类及其伴随对象
    • 它的伴生对象实现 apply 可以用作工厂方法的方法。你得到了不必使用新关键字的语法优势。

    因为类是不可变的,所以您可以获得访问器,访问器只是类的变量(或属性),而不是变种(因此无法更改变量)。构造函数参数将自动作为公共只读字段提供给您。使用起来比javabean构造好得多。

    • 你也会得到 hashCode equals ,和 toString 方法从结构上比较对象。A. copy 方法是为了能够克隆一个对象而生成的(有些字段具有提供给该方法的新值)。

    unapply 方法,该方法允许您解构case类以提取其字段。


    本质上,当您创建case类(或者如果您的类不带参数,则创建case对象)时,您从Scala得到的是一个单例对象,它的作用是 工厂 提取器 .

        7
  •  10
  •   DeepakKg    8 年前

    class case class

    1. Case Class 不需要明确的 new ,而类需要用 新的

    val classInst = new MyClass(...)  // For classes
    val classInst = MyClass(..)       // For case class
    

    2.默认情况下,构造函数参数在 案例类

    // For class
    class MyClass(x:Int) { }
    val classInst = new MyClass(10)
    
    classInst.x   // FAILURE : can't access
    
    // For caseClass
    case class MyClass(x:Int) { }
    val classInst = MyClass(10)
    
    classInst.x   // SUCCESS
    

    3. 案例类 按价值比较

    // case Class
    class MyClass(x:Int) { }
    
    val classInst = new MyClass(10)
    val classInst2 = new MyClass(10)
    
    classInst == classInst2 // FALSE
    
    // For Case Class
    case class MyClass(x:Int) { }
    
    val classInst = MyClass(10)
    val classInst2 = MyClass(10)
    
    classInst == classInst2 // TRUE
    
        8
  •  7
  •   Philipp Claßen    9 年前

    documentation

    案例类只是常规类,它们是:

    • 默认不可变
    • 可分解通过 pattern matching
    • 用结构等式而不是参照来比较
    • 简洁的实例化和操作

    另一个特点是 案例 关键字是编译器自动为我们生成的几种方法,包括Java中熟悉的toString、equals和hashCode方法。

        9
  •  7
  •   Matthew I.    7 年前

    假设以下case类定义:

    case class Foo(foo:String, bar: Int)
    

    然后在终端中执行以下操作:

    $ scalac -print src/main/scala/Foo.scala
    

    ...
    case class Foo extends Object with Product with Serializable {
    
      <caseaccessor> <paramaccessor> private[this] val foo: String = _;
    
      <stable> <caseaccessor> <accessor> <paramaccessor> def foo(): String = Foo.this.foo;
    
      <caseaccessor> <paramaccessor> private[this] val bar: Int = _;
    
      <stable> <caseaccessor> <accessor> <paramaccessor> def bar(): Int = Foo.this.bar;
    
      <synthetic> def copy(foo: String, bar: Int): Foo = new Foo(foo, bar);
    
      <synthetic> def copy$default$1(): String = Foo.this.foo();
    
      <synthetic> def copy$default$2(): Int = Foo.this.bar();
    
      override <synthetic> def productPrefix(): String = "Foo";
    
      <synthetic> def productArity(): Int = 2;
    
      <synthetic> def productElement(x$1: Int): Object = {
        case <synthetic> val x1: Int = x$1;
            (x1: Int) match {
                case 0 => Foo.this.foo()
                case 1 => scala.Int.box(Foo.this.bar())
                case _ => throw new IndexOutOfBoundsException(scala.Int.box(x$1).toString())
            }
      };
    
      override <synthetic> def productIterator(): Iterator = scala.runtime.ScalaRunTime.typedProductIterator(Foo.this);
    
      <synthetic> def canEqual(x$1: Object): Boolean = x$1.$isInstanceOf[Foo]();
    
      override <synthetic> def hashCode(): Int = {
         <synthetic> var acc: Int = -889275714;
         acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Foo.this.foo()));
         acc = scala.runtime.Statics.mix(acc, Foo.this.bar());
         scala.runtime.Statics.finalizeHash(acc, 2)
      };
    
      override <synthetic> def toString(): String = scala.runtime.ScalaRunTime._toString(Foo.this);
    
      override <synthetic> def equals(x$1: Object): Boolean = Foo.this.eq(x$1).||({
          case <synthetic> val x1: Object = x$1;
            case5(){
              if (x1.$isInstanceOf[Foo]())
                matchEnd4(true)
              else
                case6()
            };
            case6(){
              matchEnd4(false)
            };
            matchEnd4(x: Boolean){
              x
            }
        }.&&({
          <synthetic> val Foo$1: Foo = x$1.$asInstanceOf[Foo]();
          Foo.this.foo().==(Foo$1.foo()).&&(Foo.this.bar().==(Foo$1.bar())).&&(Foo$1.canEqual(Foo.this))
      }));
    
      def <init>(foo: String, bar: Int): Foo = {
        Foo.this.foo = foo;
        Foo.this.bar = bar;
        Foo.super.<init>();
        Foo.super./*Product*/$init$();
        ()
      }
    };
    
    <synthetic> object Foo extends scala.runtime.AbstractFunction2 with Serializable {
    
      final override <synthetic> def toString(): String = "Foo";
    
      case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
    
      case <synthetic> def unapply(x$0: Foo): Option =
         if (x$0.==(null))
            scala.None
         else
            new Some(new Tuple2(x$0.foo(), scala.Int.box(x$0.bar())));
    
      <synthetic> private def readResolve(): Object = Foo;
    
      case <synthetic> <bridge> <artifact> def apply(v1: Object, v2: Object): Object = Foo.this.apply(v1.$asInstanceOf[String](), scala.Int.unbox(v2));
    
      def <init>(): Foo.type = {
        Foo.super.<init>();
        ()
      }
    }
    ...
    

    如我们所见,Scala编译器生成一个正则类 Foo 和伴生物体 .

    让我们浏览编译后的类,并对我们得到的内容进行评论:

    • 系统的内部状态
    val foo: String
    val bar: Int
    
    • 吸气剂:
    def foo(): String
    def bar(): Int
    
    • 复制方法:
    def copy(foo: String, bar: Int): Foo
    def copy$default$1(): String
    def copy$default$2(): Int
    
    • 实施 scala.Product 特质:
    override def productPrefix(): String
    def productArity(): Int
    def productElement(x$1: Int): Object
    override def productIterator(): Iterator
    
    • scala.Equals 使case类实例在相等性方面具有可比性的特性 ==
    def canEqual(x$1: Object): Boolean
    override def equals(x$1: Object): Boolean
    
    • 最重要的 java.lang.Object.hashCode
    override <synthetic> def hashCode(): Int
    
    • 最重要的 java.lang.Object.toString :
    override def toString(): String
    
    • new 关键字:
    def <init>(foo: String, bar: Int): Foo 
    

    对象Foo: -方法 apply 新的 关键字:

    case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
    
    • 提取器法 unupply 对于在模式匹配中使用case类Foo:
    case <synthetic> def unapply(x$0: Foo): Option
    
    • 方法来保护作为单例的对象不被反序列化,因为它不允许再生成一个实例:
    <synthetic> private def readResolve(): Object = Foo;
    
    • scala.runtime.AbstractFunction2 做这种把戏:
    scala> case class Foo(foo:String, bar: Int)
    defined class Foo
    
    scala> Foo.tupled
    res1: ((String, Int)) => Foo = scala.Function2$$Lambda$224/1935637221@9ab310b
    

    tupled from对象返回一个函数,通过应用由2个元素组成的元组来创建一个新的Foo。

    所以case类就是语法糖。

        10
  •  5
  •   myuce    9 年前

    班级:

    scala> class Animal(name:String)
    defined class Animal
    
    scala> val an1 = new Animal("Padddington")
    an1: Animal = Animal@748860cc
    
    scala> an1.name
    <console>:14: error: value name is not a member of Animal
           an1.name
               ^
    

    但如果我们使用相同的代码但使用用例类:

    scala> case class Animal(name:String)
    defined class Animal
    
    scala> val an2 = new Animal("Paddington")
    an2: Animal = Animal(Paddington)
    
    scala> an2.name
    res12: String = Paddington
    
    
    scala> an2 == Animal("fred")
    res14: Boolean = false
    
    scala> an2 == Animal("Paddington")
    res15: Boolean = true
    

    人员类别:

    scala> case class Person(first:String,last:String,age:Int)
    defined class Person
    
    scala> val harry = new Person("Harry","Potter",30)
    harry: Person = Person(Harry,Potter,30)
    
    scala> harry
    res16: Person = Person(Harry,Potter,30)
    scala> harry.first = "Saily"
    <console>:14: error: reassignment to val
           harry.first = "Saily"
                       ^
    scala>val saily =  harry.copy(first="Saily")
    res17: Person = Person(Saily,Potter,30)
    
    scala> harry.copy(age = harry.age+1)
    res18: Person = Person(Harry,Potter,31)
    

    模式匹配:

    scala> harry match {
         | case Person("Harry",_,age) => println(age)
         | case _ => println("no match")
         | }
    30
    
    scala> res17 match {
         | case Person("Harry",_,age) => println(age)
         | case _ => println("no match")
         | }
    no match
    

    对象:单例:

    scala> case class Person(first :String,last:String,age:Int)
    defined class Person
    
    scala> object Fred extends Person("Fred","Jones",22)
    defined object Fred
    
        11
  •  4
  •   Slow Harry    9 年前

    tupled 防御,有一种类型:

    case class Person(name: String, age: Int)
    //Person.tupled is def tupled: ((String, Int)) => Person
    

    我能找到的唯一用例是当您需要从tuple构造case类时,例如:

    val bobAsTuple = ("bob", 14)
    val bob = (Person.apply _).tupled(bobAsTuple) //bob: Person = Person(bob,14)
    

    通过直接创建对象,您可以在没有tuple的情况下执行相同的操作,但是如果您的数据集表示为具有arity20的tuple列表(具有20个元素的tuple),那么您可以选择使用tuple。

        12
  •  4
  •   Cody Gray Felix    8 年前

    与类不同,case类只是用来保存数据。

    Case类对于以数据为中心的应用程序是灵活的,这意味着您可以在Case类中定义数据字段,并在伴随对象中定义业务逻辑。通过这种方式,您将数据与业务逻辑分离。

    使用copy方法,您可以从源代码继承任何或所有必需的属性,并可以根据需要对其进行更改。

        13
  •  3
  •   18446744073709551615    9 年前

    A. 案例类 是一个可以与 match/case 陈述

    def isIdentityFun(term: Term): Boolean = term match {
      case Fun(x, Var(y)) if x == y => true
      case _ => false
    }
    

    你看到了吗 case 后面是class Fun的实例,它的第二个参数是Var。这是一个非常好而且强大的语法,但是它不能处理任何类的实例,因此case类有一些限制。如果遵守这些限制,就可以自动定义hashcode和equals。

    模糊短语“通过模式匹配的递归分解机制”的意思是“它与 ". (事实上,接下来的例子 match 与下面的实例相比较 ,Scala必须同时分解它们,并且必须递归地分解它们的组成部分。)

    什么 案例类别 对你有用吗?这个 Wikipedia article about Algebraic Data Types 给出了两个很好的经典例子,列表和树。支持代数数据类型(包括知道如何比较它们)是任何现代函数式语言都必须具备的。

    什么 案例类别 对你有用吗?有些对象有状态,比如 connection.setConnectTimeout(connectTimeout) 不适用于案例类。

    现在你可以阅读了 A Tour of Scala: Case Classes

        14
  •  3
  •   arglee    7 年前

    我认为所有的答案都给出了关于类和案例类的语义解释。 这可能非常相关,但是scala中的每个新手都应该知道创建case类时会发生什么。我已经写信了 this 答案,简单地解释了案例类。

    每个程序员都应该知道,如果他们使用的是任何预构建的函数,那么他们编写的代码就相对较少,这使得他们能够编写最优化的代码,但是这种能力带来了巨大的责任。所以,使用预建函数时要非常小心。

    一些开发人员避免编写case类,因为还有20个方法,您可以通过反汇编类文件看到这些方法。

    refer this link if you want to check all the methods inside a case class .

        15
  •  1
  •   tictactoki    9 年前
    • Case类使用apply和unapply方法定义compagnon对象
    • Case类扩展了可序列化的
    • Case类定义hashCode和copy方法
    • 构造函数的所有属性都是val(语法糖)
        16
  •  1
  •   Krishnadas PC    6 年前

    case classes 如下所示

    1. 您可以实例化case类而不必 new 关键字。

    scala fiddle上的scala代码示例,取自scala文档。

    https://scalafiddle.io/sf/34XEQyE/0

        17
  •  0
  •   ComDubh    4 年前

    先前的答复中没有提到的一个重要问题是 身份