494 Stimmen

Was ist der Unterschied zwischen Scalas Fallklasse und Klasse?

Ich suchte in Google nach den Unterschieden zwischen einer case class und eine class . Jeder erwähnt, dass, wenn Sie Musterabgleich auf die Klasse tun wollen, verwenden Sie Fallklasse. Andernfalls verwenden Sie Klassen und auch die Erwähnung einige zusätzliche Vorteile wie Gleichheit und Hash-Code überschreiben. Aber sind dies die einzigen Gründe, warum man eine Fallklasse anstelle einer Klasse verwenden sollte?

Ich vermute, dass es einen sehr wichtigen Grund für diese Funktion in Scala geben muss. Was ist die Erklärung oder gibt es eine Ressource, um mehr über die Scala Fallklassen zu lernen?

18voto

Faktor 10 Punkte 1660

Das Fallklassenkonstrukt in Scala kann auch als eine Bequemlichkeit gesehen werden, um einige Boilerplate zu entfernen.

Bei der Konstruktion einer Case-Klasse gibt Scala folgendes vor.

  • Er erstellt eine Klasse und ihr Begleitobjekt
  • Sein Begleitobjekt implementiert die apply Methode, die Sie als Fabrikmethode verwenden können. Dies hat den syntaktischen Vorteil, dass Sie das Schlüsselwort new nicht verwenden müssen.

Da die Klasse unveränderlich ist, erhalten Sie Accessors, die nur die Variablen (oder Eigenschaften) der Klasse sind, aber keine Mutators (also keine Möglichkeit, die Variablen zu ändern). Die Konstruktorparameter stehen Ihnen automatisch als öffentliche schreibgeschützte Felder zur Verfügung. Viel angenehmer zu verwenden als das Java-Bean-Konstrukt.

  • Sie erhalten außerdem hashCode , equals y toString Methoden standardmäßig und die equals Methode vergleicht ein Objekt strukturell. A copy Methode generiert, um ein Objekt zu klonen (wobei einige Felder neue Werte haben, die der Methode übergeben werden).

Der größte Vorteil ist, wie bereits erwähnt, die Tatsache, dass Sie Mustervergleiche für Fallklassen durchführen können. Der Grund dafür ist, dass Sie die unapply Methode, mit der Sie eine Fallklasse dekonstruieren können, um ihre Felder zu extrahieren.


Im Wesentlichen erhalten Sie von Scala bei der Erstellung einer Case-Klasse (oder eines Case-Objekts, wenn Ihre Klasse keine Argumente annimmt) ein Singleton-Objekt, das den Zweck eines Werk und als Abzieher .

15voto

DeepakKg Punkte 299

Abgesehen von dem, was bereits gesagt wurde, gibt es einige weitere grundlegende Unterschiede zwischen class y case class

1. Case Class muss nicht explizit new aufzurufen ist, während die Klasse mit new

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

2. standardmäßig sind die Parameter der Konstruktoren privat in class während die Öffentlichkeit 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 sich nach ihrem Wert vergleichen

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

11voto

Matthew I. Punkte 1638

Das ultimative Verständnis dafür zu haben, was eine Fallklasse ist:

Gehen wir von der folgenden Fallklassendefinition aus:

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

und führen Sie dann im Terminal folgende Schritte aus:

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

Scala 2.12.8 wird ausgegeben:

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

Wie wir sehen können, erzeugt der Scala-Compiler eine reguläre Klasse Foo und Begleiter-Objekt Foo .

Gehen wir die kompilierte Klasse durch und kommentieren wir, was wir bekommen haben:

  • den internen Zustand des Foo Klasse, unveränderlich:

    val foo: String val bar: Int

  • Getter:

    def foo(): String def bar(): Int

  • Kopiermethoden:

    def copy(foo: String, bar: Int): Foo def copy$default$1(): String def copy$default$2(): Int

  • Implementierung scala.Product Eigenschaft:

    override def productPrefix(): String def productArity(): Int def productElement(x$1: Int): Object override def productIterator(): Iterator

  • Implementierung scala.Equals Eigenschaft, um Fallklasseninstanzen auf Gleichheit zu vergleichen, indem == :

    def canEqual(x$1: Object): Boolean override def equals(x$1: Object): Boolean

  • Übergeordnetes java.lang.Object.hashCode für die Einhaltung des Gleichheits-Hashcode-Vertrags:

    override <synthetic> def hashCode(): Int

  • Übergeordnetes java.lang.Object.toString :

    override def toString(): String

  • Konstruktor für die Instanziierung durch new Stichwort:

    def <init>(foo: String, bar: Int): Foo

Objekt Foo: - Methode apply zur Instanziierung ohne new Stichwort:

case <synthetic> def apply(foo: String, bar: Int): Foo = new Foo(foo, bar);
  • Extraktionsverfahren unupply für die Verwendung der Fallklasse Foo beim Mustervergleich:

    case <synthetic> def unapply(x$0: Foo): Option

  • Methode, um das Objekt als Singleton vor der Deserialisierung zu schützen, damit nicht noch eine weitere Instanz erzeugt werden kann:

    <synthetic> private def readResolve(): Object = Foo;

  • Objekt Foo erweitert scala.runtime.AbstractFunction2 für einen solchen 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 gibt eine Funktion zur Erstellung eines neuen Foo durch Anwendung eines Tupels aus 2 Elementen zurück.

Die Fallklasse ist also nur syntaktischer Zucker.

7voto

Nach Scalas Dokumentation :

Fallklassen sind ganz normale Klassen, die sind:

  • Standardmäßig unveränderlich
  • Zersetzbar durch Mustervergleich
  • Vergleich durch strukturelle Gleichheit statt durch Referenz
  • Kurz und bündig zu instanziieren und zu bearbeiten

Ein weiteres Merkmal der Fall Schlüsselwort ist, generiert der Compiler automatisch mehrere Methoden für uns, einschließlich der bekannten toString-, equals- und hashCode-Methoden in Java.

6voto

user1668782 Punkte 159

Klasse:

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
           ^

Aber wenn wir denselben Code verwenden, aber die Klasse case:

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

Klasse Person:

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)

Mustervergleiche:

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

Objekt: 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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X