简体   繁体   English

了解Scala中的Case类和Traits

[英]Understanding Case class and Traits in Scala

I have a simple trait as defined below: 我有一个如下定义的简单特征:

trait MyTrait {

  def myStringVal: String

}

My case class which implements this trait is as below: 我的case类实现了这个特性如下:

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. 来自Java世界,我发现有点难以理解MyCaseClass实际上只是通过定义MyCaseClass的参数来实现它。 I understand that thy byte code would actually write the getter and setter. 我知道你的字节代码实际上会编写getter和setter。 But how is this possible without any var or val? 但如果没有var或val,这怎么可能呢?

My understanding is that if there is no var or val, then there is no getter or setter method generated. 我的理解是,如果没有var或val,则不会生成getter或setter方法。 In that case how is the above case class MyCaseClass implementing myStringVal method? 在这种情况下,上面的案例类MyCaseClass如何实现myStringVal方法?

Sometime too much of this Scala magic is difficult to understand especially with legacy code. 有时太多的Scala魔法难以理解,特别是遗留代码。

You might want to check out this blog article covering what case classes exactly are and why they are so useful. 您可能希望查看此博客文章,其中涵盖了具体类型以及它们如此有用的原因。

In your example, the trait MyTrait has no use, except being able to function like a java interface. 在您的示例中,特性MyTrait没有用,除了能够像java接口一样运行。 Note, that the default visibility in scala is public. 请注意,scala中的默认可见性是公共的。 By default case class parameters are immutable so in your example val is automatically inferred by the compiler for the myStringVal argument. 默认情况下,case类参数是不可变的,因此在您的示例中, val会由编译器自动推断为myStringVal参数。

What magic do case classes do?! 案例课有什么神奇之处 ?!

  • Convert all constructor parameters to public readonly ( val ) by default fields 默认字段将所有构造函数参数转换为public readonly( val
  • Generate the toString() , equals() and hashcode() methods using all constructor params for each method 使用每个方法的所有构造函数参数生成toString()equals()hashcode()方法
  • 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 new keyword and an extractor which by default generates an option-wrapped tuple of the case class parameters. 生成具有相同名称的伴随对象,其中包含适当的apply()unapply()方法,它们基本上只是一个方便构造函数,允许在不使用new关键字的情况下实例化,并且默认情况下会生成一个包含选项包装的case类的tuple参数。

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$2();
  public int copy$default$1();
  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);
}   

Scala case classes have a plenty of boilerplate implemented for you, and having all the constructor parameters automatically exposed as val s is one of these things. Scala案例类为您实现了大量的样板,并且将所有构造函数参数自动暴露为val这是其中之一。

If you try avoiding val s in a regular class, like that: 如果你试图在常规类中避免使用val ,那样:

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 myVal method, but adding val or var to the class constructor parameter will solve the issue. 编译器将显示错误消息,MyClass必须是抽象的,因为它不会覆盖myVal方法,但是将valvar添加到类构造函数参数将解决问题。

Case classes are different -- some default methods are generated for them. 案例类是不同的 - 为它们生成了一些默认方法。 This includes val getters for the parameters. 这包括参数的val getters。 Think of case classes as POJOs -- this is a useful bit of syntactic sugar, since they don't need private members. 将案例类视为POJO - 这是一个有用的语法糖,因为它们不需要私有成员。

Some other useful methods are generated too, for example copy , toString , apply and unapply . unapply生成一些其他有用的方法,例如copytoStringapplyunapply

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM