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

发布于 2024-08-22 07:22:43 字数 222 浏览 4 评论 0原文

我在 Google 中搜索了 case classclass 之间的差异。每个人都提到,当你想在类上进行模式匹配时,请使用用例类。否则使用类并提及一些额外的好处,例如等于和哈希码覆盖。但是这些是人们应该使用案例类而不是类的唯一原因吗?

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

I searched in Google to find the differences between a case class and a class. Everyone mentions that when you want to do pattern matching on the class, use case class. Otherwise use classes and also mentioning some extra perks like equals and hash code overriding. But are these the only reasons why one should use a case class instead of class?

I guess there should be some very important reason for this feature in Scala. What is the explanation or is there a resource to learn more about the Scala case classes from?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

一梦浮鱼 2024-08-29 07:22:44

从技术上讲,类和案例类之间没有区别——即使编译器在使用案例类时确实优化了一些东西。但是,案例类用于消除特定模式的样板,该模式正在实现代数数据类型< /a>.

这种类型的一个非常简单的例子是树。例如,二叉树可以这样实现:

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 =>
}

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

它们还可以与哈希映射或集合一起使用,因为它们具有有效、稳定的哈希码。

Technically, there is no difference between a class and a case class -- even if the compiler does optimize some stuff when using case classes. However, a case class is used to do away with boiler plate for a specific pattern, which is implementing algebraic data types.

A very simple example of such types are trees. A binary tree, for instance, can be implemented like this:

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

That enable us to do the following:

// 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 =>
}

Note that trees construct and deconstruct (through pattern match) with the same syntax, which is also exactly how they are printed (minus spaces).

And they can also be used with hash maps or sets, since they have a valid, stable hashCode.

剩一世无双 2024-08-29 07:22:44
  • Case 类可以进行模式匹配
  • Case 类自动定义 hashcode 和 equals
  • Case 类自动为构造函数参数定义 getter 方法。

(除了最后一个之外,您已经提到了所有内容)。

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

  • Case classes can be pattern matched
  • Case classes automatically define hashcode and equals
  • Case classes automatically define getter methods for the constructor arguments.

(You already mentioned all but the last one).

Those are the only differences to regular classes.

紙鸢 2024-08-29 07:22:44

没有人提到案例类具有 val 构造函数参数,但这也是常规类的默认设置(我认为这是 Scala 设计中的不一致)。达里奥暗示了这一点,他指出它们是“不可变的”。

请注意,您可以通过在案例类的每个构造函数参数前面添加 var 来覆盖默认值。但是,使 case 类可变会导致它们的 equalshashCode 方法随时间变化。[1]

sepp2k 已经提到案例类会自动生成 equalshashCode 方法。

也没有人提到案例类会自动创建一个与类同名的伴随对象,其中包含applyunapply方法。 apply 方法允许构造实例,而无需在前面添加 newunapply 提取器方法启用其他人提到的模式匹配。

此外,编译器还优化了 case 类的 match-case 模式匹配的速度[2]。

[1] 案例类很酷

[2] 案例类和提取器,第 15 页

No one mentioned that case classes have val constructor parameters yet this is also the default for regular classes (which I think is an inconsistency in the design of Scala). Dario implied such where he noted they are "immutable".

Note you can override the default by prepending the each constructor argument with var for case classes. However, making case classes mutable causes their equals and hashCode methods to be time variant.[1]

sepp2k already mentioned that case classes automatically generate equals and hashCode methods.

Also no one mentioned that case classes automatically create a companion object with the same name as the class, which contains apply and unapply methods. The apply method enables constructing instances without prepending with new. The unapply extractor method enables the pattern matching that others mentioned.

Also the compiler optimizes the speed of match-case pattern matching for case classes[2].

[1] Case Classes Are Cool

[2] Case Classes and Extractors, pg 15.

清君侧 2024-08-29 07:22:44

没有人提到案例类也是 Product 的实例,因此继承了这些方法:

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

其中 productArity 返回类参数的数量,productElement(i) 返回 ith 参数,而 productIterator 允许迭代它们。

No one mentioned that case classes are also instances of Product and thus inherit these methods:

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

where the productArity returns the number of class parameters, productElement(i) returns the ith parameter, and productIterator allows iterating through them.

音栖息无 2024-08-29 07:22:44

Scala 中的案例类构造也可以被视为删除一些样板文件的便利。

当构建案例类时,Scala 会为您提供以下内容。

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

因为类是不可变的,所以您可以获得访问器,它们只是类的变量(或属性),但没有修改器(因此无法更改变量)。构造函数参数将作为公共只读字段自动提供给您。比 Java bean 构造好用得多。

  • 默认情况下,您还可以获得 hashCodeequalstoString 方法,并且 equals 方法从结构上比较对象。生成复制方法以能够克隆对象(某些字段具有提供给该方法的新值)。

前面提到的最大优点是您可以对案例类进行模式匹配。这样做的原因是因为您获得了 unapply 方法,该方法允许您解构案例类以提取其字段。


本质上,在创建案例类(或者如果您的类不带参数,则为案例对象)时,您从 Scala 获得的是一个单例对象,其用作工厂和提取器。

The case class construct in Scala can also be seen as a convenience to remove some boilerplate.

When constructing a case class Scala gives you the following.

  • It creates a class as well as its companion object
  • Its companion object implements the apply method that you are able to use as a factory method. You get the syntactic sugar advantage of not having to use the new keyword.

Because the class is immutable you get accessors, which are just the variables (or properties) of the class but no mutators (so no ability to change the variables). The constructor parameters are automatically available to you as public read only fields. Much nicer to use than Java bean construct.

  • You also get hashCode, equals, and toString methods by default and the equals method compares an object structurally. A copy method is generated to be able to clone an object (with some fields having new values provided to the method).

The biggest advantage as has been mentioned previously is the fact that you can pattern match on case classes. The reason for this is because you get the unapply method which lets you deconstruct a case class to extract its fields.


In essence what you are getting from Scala when creating a case class (or a case object if your class takes no arguments) is a singleton object which serves the purpose as a factory and as an extractor .

一袭白衣梦中忆 2024-08-29 07:22:44

除了人们已经说过的之外,classcase class 之间还有一些更基本的区别

1.Case Class 不需要显式 < code>new,而类需要使用 new 调用

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

2.默认情况下,构造函数参数在 class 中是私有的,而在 case class 中是公共的

// 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 按值比较自身

// For 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

Apart from what people have already said, there are some more basic differences between class and case class

1.Case Class doesn't need explicit new, while class need to be called with new

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

2.By Default constructors parameters are private in class , while its public in case class

// 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 compare themselves by value

// For 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
兮颜 2024-08-29 07:22:44

为了最终了解什么是案例类:

让我们假设以下案例类定义:

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

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

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

Scala 2.12.8 将输出:

...
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 和伴生对象 Foo

让我们浏览一下编译后的类并评论我们得到的内容:

  • Foo 类的内部状态,不可变:
val foo: String
val bar: Int
  • getters:
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 特征,用于通过 == 使案例类实例具有可比性:
def canEqual(x$1: Object): Boolean
override def equals(x$1: Object): Boolean
  • 重写 java.lang.Object.hashCode 以遵守 equals- hashcode 契约:
override <synthetic> def hashCode(): Int
  • 重写 java.lang.Object.toString :
override def toString(): String
  • 通过 new 关键字实例化的构造函数:
def <init>(foo: String, bar: Int): Foo 

Object Foo:
- 方法 apply 用于实例化,无需 new 关键字:

case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
  • 提取器方法 unupply 用于在模式匹配中使用案例类 Foo:
case <synthetic> def unapply(x$0: Foo): Option
  • 保护对象作为单例免遭反序列化的方法为了不让再产生一个实例:
<synthetic> private def readResolve(): Object = Foo;
  • object Foo extends 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 object 返回一个函数,通过应用元组来创建一个新的 Foo 2 个元素。

所以案例类只是语法糖。

To have the ultimate understanding of what is a case class:

let's assume the following case class definition:

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

and then do the following in the terminal:

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

Scala 2.12.8 will output:

...
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>();
    ()
  }
}
...

As we can see Scala compiler produces a regular class Foo and companion-object Foo.

Let's go through the compiled class and comment on what we have got:

  • the internal state of the Foo class, immutable:
val foo: String
val bar: Int
  • getters:
def foo(): String
def bar(): Int
  • copy methods:
def copy(foo: String, bar: Int): Foo
def copy$default$1(): String
def copy$default$2(): Int
  • implementing scala.Product trait:
override def productPrefix(): String
def productArity(): Int
def productElement(x$1: Int): Object
override def productIterator(): Iterator
  • implementing scala.Equals trait for make case class instances comparable for equality by ==:
def canEqual(x$1: Object): Boolean
override def equals(x$1: Object): Boolean
  • overriding java.lang.Object.hashCode for obeying the equals-hashcode contract:
override <synthetic> def hashCode(): Int
  • overriding java.lang.Object.toString :
override def toString(): String
  • constructor for instantiation by new keyword:
def <init>(foo: String, bar: Int): Foo 

Object Foo:
- method apply for instantiation without new keyword:

case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
  • extractor method unupply for using case class Foo in pattern matching:
case <synthetic> def unapply(x$0: Foo): Option
  • method to protect object as singleton from deserialization for not letting produce one more instance:
<synthetic> private def readResolve(): Object = Foo;
  • object Foo extends scala.runtime.AbstractFunction2 for doing such trick:
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 object returns a funtion to create a new Foo by applying a tuple of 2 elements.

So case class is just syntactic sugar.

将军与妓 2024-08-29 07:22:44

根据 Scala 的文档

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

  • 默认情况下不可变
  • 可通过模式匹配进行分解
  • 通过结构相等而不是通过引用进行比较
  • 简洁的实例化和操作

case 关键字的另一个特点是编译器会自动为我们生成几个方法,包括我们熟悉的 toString、equals 和 hashCode 方法在爪哇。

According to Scala's documentation:

Case classes are just regular classes that are:

  • Immutable by default
  • Decomposable through pattern matching
  • Compared by structural equality instead of by reference
  • Succinct to instantiate and operate on

Another feature of the case keyword is the compiler automatically generates several methods for us, including the familiar toString, equals, and hashCode methods in Java.

鹿! 2024-08-29 07:22:44

类:

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

Class:

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
           ^

But if we use same code but use case class:

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

Person class:

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)

Pattern Matching:

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

object: singleton:

scala> case class Person(first :String,last:String,age:Int)
defined class Person

scala> object Fred extends Person("Fred","Jones",22)
defined object Fred
暮年慕年 2024-08-29 07:22:44

没有人提到 case 类伴随对象有 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)

你可以做同样的事情,而不需要tupled,通过直接创建对象来表示,但如果您的数据集表示为元数为 20 的元组列表(具有 20 个元素的元组),则可能会选择使用 tupled。

Nobody mentioned that case class companion object has tupled defention, which has a type:

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

The only use case I can find is when you need to construct case class from tuple, example:

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

You can do the same, without tupled, by creating object directly, but if your datasets expressed as list of tuple with arity 20(tuple with 20 elements), may be using tupled is your choise.

扛刀软妹 2024-08-29 07:22:44

与类不同,案例类仅用于保存数据。

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

使用复制方法,您可以从源继承任何或所有必需的属性,并可以根据需要更改它们。

Unlike classes, case classes are just used to hold data.

Case classes are flexible for data-centric applications, which means you can define data fields in case class and define business logic in a companion object. In this way, you are separating the data from the business logic.

With the copy method, you can inherit any or all required properties from the source and can change them as you like.

梦巷 2024-08-29 07:22:44

case 类是可以与 match/case 语句一起使用的类。

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

您会看到 case 后面跟着一个 Fun 类的实例,其第二个参数是 Var。这是一个非常好的和强大的语法,但它不能用于任何类的实例,因此对案例类有一些限制。如果遵守这些限制,就可以自动定义 hashcode 和 equals。

模糊的短语“通过模式匹配的递归分解机制”意味着“它适用于 case”。 (事实上​​,match 后面的实例与 case 后面的实例进行比较(匹配),Scala 必须分解它们,并且必须递归地分解它们是什么组成。)

案例类有什么用处? 有关代数数据类型的维基百科文章给出了两个很好的经典示例:列表和树。对代数数据类型的支持(包括知道如何比较它们)是任何现代函数语言都必须的。

哪些案例类没有有用?有些对象具有状态,像 connection.setConnectTimeout(connectTimeout) 这样的代码不适用于案例类。

现在您可以阅读 Scala 之旅:案例类

A case class is a class that may be used with the match/case statement.

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

You see that case is followed by an instance of class Fun whose 2nd parameter is a Var. This is a very nice and powerful syntax, but it cannot work with instances of any class, therefore there are some restrictions for case classes. And if these restrictions are obeyed, it is possible to automatically define hashcode and equals.

The vague phrase "a recursive decomposition mechanism via pattern matching" means just "it works with case". (Indeed, the instance followed by match is compared to (matched against) the instance that follows case, Scala has to decompose them both, and has to recursively decompose what they are made of.)

What case classes are useful for? The Wikipedia article about Algebraic Data Types gives two good classical examples, lists and trees. Support for algebraic data types (including knowing how to compare them) is a must for any modern functional language.

What case classes are not useful for? Some objects have state, the code like connection.setConnectTimeout(connectTimeout) is not for case classes.

And now you can read A Tour of Scala: Case Classes

栀梦 2024-08-29 07:22:44

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

每个程序员都应该知道,如果他们使用任何预构建的函数,那么他们编写的代码相对较少,这使他们能够编写最优化的代码,但权力伴随着巨大的责任。因此,使用预构建函数时要非常小心。

一些开发人员由于额外的 20 个方法而避免编写案例类,您可以通过反汇编类文件看到这些方法。

如果您想检查案例类中的所有方法,请参考此链接

I think overall all the answers have given a semantic explanation about classes and case classes.
This could be very much relevant, but every newbie in scala should know what happens when you create a case class. I have written this answer, which explains case class in a nutshell.

Every programmer should know that if they are using any pre-built functions, then they are writing a comparatively less code, which is enabling them by giving the power to write most optimized code, but power comes with great responsibilities. So, use prebuilt functions with very cautions.

Some developers avoid writing case classes due to additional 20 methods, which you can see by disassembling class file.

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

听,心雨的声音 2024-08-29 07:22:44

前面的答案中没有提到的一个重要问题是身份。常规类的对象具有标识,因此即使两个对象的所有字段具有相同的值,它们仍然是不同的对象。然而,对于案例类实例,相等性纯粹是根据对象字段的值来定义的。

One important issue not mentioned in earlier answers is that of identity. Objects of regular classes have identity, so even if two objects have identical values for all their fields, they are still different objects. For case class instances however, equality is defined in purely terms of the values of the object's fields.

深爱不及久伴 2024-08-29 07:22:44
  • Case 类定义一个带有 apply 和 unappy 方法的 compagnon 对象
  • Case 类扩展 Serialized
  • Case 类定义 equals hashCode 和 copy 方法
  • 构造函数的所有属性都是 val (语法糖)
  • Case classes define a compagnon object with apply and unapply methods
  • Case classes extends Serializable
  • Case classes define equals hashCode and copy methods
  • All attributes of the constructor are val (syntactic sugar)
小傻瓜 2024-08-29 07:22:44

下面列出了案例类的一些关键功能案例

  1. 类是不可变的。
  2. 您可以实例化没有 new 关键字的案例类。
  3. 案例类可以按值进行比较

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

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

Some of the key features of case classes are listed below

  1. case classes are immutable.
  2. You can instantiate case classes without new keyword.
  3. case classes can be compared by value

Sample scala code on scala fiddle, taken from the scala docs.

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

信仰 2024-08-29 07:22:43

案例类可以被视为简单且不可变的数据保存对象,它们应该完全依赖于它们的构造函数参数。

这个功能概念允许我们

  • 使用紧凑的初始化语法(Node(1, Leaf(2), None))
  • 使用模式匹配来分解它们
  • ,并隐式定义相等比较

与继承相结合,案例类是用于模仿代数数据类型

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

Case classes can be seen as plain and immutable data-holding objects that should exclusively depend on their constructor arguments.

This functional concept allows us to

  • use a compact initialization syntax (Node(1, Leaf(2), None)))
  • decompose them using pattern matching
  • have equality comparisons implicitly defined

In combination with inheritance, case classes are used to mimic algebraic datatypes.

If an object performs stateful computations on the inside or exhibits other kinds of complex behaviour, it should be an ordinary class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文