理解 Scala 中的 Case 类和特征

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22769175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 06:09:35  来源:igfitidea点击:

Understanding Case class and Traits in Scala

scala

提问by sparkr

I have a simple trait as defined below:

我有一个简单的特征,定义如下:

trait MyTrait {

  def myStringVal: String

}

My case class which implements this trait is as below:

我实现这个特性的案例类如下:

case class MyCaseClass(myStringVal: String) extends MyTrait {
  ...
  ...
}

Coming from a Java world, I find it a bit difficult to fathom the fact that MyCaseClass actually implements this just by defining a parameter to MyCaseClass. I understand that thy byte code would actually write the getter and setter. But how is this possible without any var or val?

来自 Java 世界,我发现很难理解 MyCaseClass 实际上只是通过为 MyCaseClass 定义一个参数来实现这一点的事实。我知道你的字节码实际上会编写 getter 和 setter。但是,如果没有任何 var 或 val,这怎么可能呢?

My understanding is that if there is no var or val, then there is no getter or setter method generated. In that case how is the above case class MyCaseClass implementing myStringVal method?

我的理解是,如果没有 var 或 val,则不会生成 getter 或 setter 方法。在那种情况下,上面的案例类 MyCaseClass 是如何实现 myStringVal 方法的?

Sometime too much of this Scala magic is difficult to understand especially with legacy code.

有时太多的 Scala 魔法很难理解,尤其是对于遗留代码。

回答by Floscher

You might want to check out this blog articlecovering what case classes exactly are and why they are so useful.

您可能想查看这篇博客文章,其中介绍了案例类究竟是什么以及它们为何如此有用。

In your example, the trait MyTraithas no use, except being able to function like a java interface. Note, that the default visibility in scala is public. By default case class parameters are immutable so in your example valis automatically inferred by the compiler for the myStringValargument.

在您的示例中,该 traitMyTrait没有用,除了能够像 java 接口一样运行。请注意,Scala 中的默认可见性是公开的。默认情况下,case 类参数是不可变的,因此在您的示例val中,编译器会自动为myStringVal参数推断出该参数。

What magicdo case classes do?!

案例类有什么魔力?!

  • Convert all constructor parameters to public readonly (val) by default fields
  • Generate the toString(), equals()and hashcode()methods using all constructor params for each method
  • Generate companion object with the same name containing an appropriate apply()and unapply()method, which are basically just a convenience constructor allowing to instantiate without using the newkeyword and an extractor which by default generates an option-wrapped tupleof the case class parameters.
  • val默认字段将所有构造函数参数转换为 public readonly ( )
  • 使用每个方法的所有构造函数参数生成toString(),equals()hashcode()方法
  • 生成包含适当apply()unapply()方法的同名伴生对象,它们基本上只是一个方便的构造函数,允许在不使用new关键字的情况下进行实例化,以及默认情况下生成选项包装tuple的 case 类参数的提取器。

EDIT:Sample compiler output for (case) classes (copied from scalatutorial.de)

编辑:(案例)类的示例编译器输出(从scalatutorial.de复制)

A simple scala class definition like

一个简单的 Scala 类定义,如

class A1(v1: Int, v2: Double)

gets compiled to the java code

被编译成 java 代码

public class A1 extends java.lang.Object implements scala.ScalaObject {
  public A1(int, double);
}    

the analogous case class

类似的案例类

case class A2(v1: Int, v2: Double)

gets compiled to the following java classes

被编译为以下 java 类

public class A2 extends java.lang.Object implements 
scala.ScalaObject,scala.Product,java.io.Serializable {
  public static final scala.Function1 tupled();
  public static final scala.Function1 curry();
  public static final scala.Function1 curried();
  public scala.collection.Iterator productIterator();
  public scala.collection.Iterator productElements();
  public double copy$default();
  public int copy$default();
  public int v1();
  public double v2();
  public A2 copy(int, double);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public boolean canEqual(java.lang.Object);
  public A2(int, double);
}


public final class A2$ extends scala.runtime.AbstractFunction2 
implements scala.ScalaObject {
  public static final A2$ MODULE$;
  public static {};
  public scala.Option unapply(A2);
  public A2 apply(int, double);
  public java.lang.Object apply(java.lang.Object, java.lang.Object);
}   

回答by abatyuk

Scala case classes have a plenty of boilerplate implemented for you, and having all the constructor parameters automatically exposed as vals is one of these things.

Scala 案例类为您实现了大量样板,并且将所有构造函数参数自动公开为vals 就是其中之一。

If you try avoiding vals in a regular class, like that:

如果您尝试val在常规课程中避免使用s,如下所示:

trait MyTrait {
  def myVal: String
}

class MyClass(myVal: String) extends MyTrait

Compiler will show you the error message, that MyClass has to be abstract, as it does't override myValmethod, but adding valor varto the class constructor parameter will solve the issue.

编译器将向您显示错误消息,即 MyClass 必须是抽象的,因为它不会覆盖myVal方法,但是将val或添加var到类构造函数参数将解决问题。

回答by Richard Close

Case classes are different -- some default methods are generated for them. This includes val getters for the parameters. Think of case classes as POJOs -- this is a useful bit of syntactic sugar, since they don't need private members.

案例类是不同的——为它们生成一些默认方法。这包括参数的 val getter。将案例类视为 POJO——这是一个有用的语法糖,因为它们不需要私有成员。

Some other useful methods are generated too, for example copy, toString, applyand unapply.

还生成了一些其他有用的方法,例如copytoStringapplyunapply