理解Scala中的案例类别和特征

理解Scala中的案例类别和特征,scala,Scala,我有一个简单的特点,定义如下: trait MyTrait { def myStringVal: String } 我实现此特性的case类如下所示: case class MyCaseClass(myStringVal: String) extends MyTrait { ... ... } 来自Java世界,我发现仅仅通过定义MyCaseClass的一个参数来理解MyCaseClass实际上实现了这一点有点困难。我知道你的字节码实际上会编写getter和setter。但是

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

trait MyTrait {

  def myStringVal: String

}
我实现此特性的case类如下所示:

case class MyCaseClass(myStringVal: String) extends MyTrait {
  ...
  ...
}
来自Java世界,我发现仅仅通过定义MyCaseClass的一个参数来理解MyCaseClass实际上实现了这一点有点困难。我知道你的字节码实际上会编写getter和setter。但是,没有任何var或val,这怎么可能呢

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


有时候,Scala的魔力太多了,特别是对于遗留代码,很难理解。

Case类是不同的——会为它们生成一些默认方法。这包括参数的val getter。把case类想象成pojo——这是一个有用的语法糖,因为它们不需要私有成员


还生成了一些其他有用的方法,例如
copy
toString
apply
unapply
Scala案例类为您实现了大量的样板文件,并且将所有构造函数参数自动公开为
val
s就是其中之一

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

trait MyTrait {
  def myVal: String
}

class MyClass(myVal: String) extends MyTrait

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

您可能希望查看哪些案例类以及它们为什么如此有用

在您的示例中,trait
MyTrait
除了能够像java接口一样工作外,没有任何用处。请注意,scala中的默认可见性是公共的。默认情况下,case类参数是不可变的,因此在您的示例中,
val
由编译器为
myStringVal
参数自动推断

案例课有什么魔力

  • 默认情况下,将所有构造函数参数转换为公共只读(
    val
  • 使用每个方法的所有构造函数参数生成
    toString()
    equals()
    hashcode()
    方法
  • 生成同名的伴随对象,其中包含适当的
    apply()
    unapply()
    方法,基本上,它只是一个方便的构造函数,允许在不使用
    new
    关键字的情况下实例化,并且是一个提取器,默认情况下,提取器会生成一个案例类参数的选项包装的
    tuple
编辑:示例(case)类的编译器输出(复制自)

一个简单的scala类定义,如

class A1(v1: Int, v2: Double)
编译为java代码

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

case class A2(v1: Int, v2: Double)
编译为以下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);
}   

如果我为我的case类编写自己的同伴对象呢?我想这也是可以接受的?案例类可以扩展吗?我想这不是因为它们是不可变的,编写自己的伴奏对象应该可以,但是我个人会使用默认值作为参数。case类本身不是不可变的,我将使用类/case类的编译器输出更新我的答案。根本没有测试过从case类继承-您需要在父类型上进行模式匹配吗?我在这里不是专家——也许其他人可以进一步阐述?从traits(抽象)类等继承是可以的。但是,您需要在case类构造函数中指定父类的每个公共属性。给定的链接不起作用。因此,从archive.org获取: