Понимание класса и черт класса в Scala

У меня есть простая черта, как определено ниже:

trait MyTrait {

  def myStringVal: String

}

Мой класс case, который реализует этот признак, выглядит следующим образом:

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

Исходя из мира Java, мне трудно понять, что MyCaseClass действительно реализует это, просто определяя параметр MyCaseClass. Я понимаю, что ваш байт-код на самом деле будет писать геттер и сеттер. Но как это возможно без каких-либо var или val?

Я понимаю, что если нет var или val, то не будет создан метод getter или setter. В этом случае, как приведенный выше класс case MyCaseClass реализует метод myStringVal?

Иногда слишком много этой магии Scala трудно понять, особенно с устаревшим кодом.

Ответ 1

Возможно, вы захотите проверить эту статью в блоге, в которой описаны классы случаев и почему они так полезны.

В вашем примере признак MyTrait не имеет смысла, кроме возможности работать как java-интерфейс. Обратите внимание, что видимость по умолчанию в scala является общедоступной. По умолчанию параметры класса case неизменяемы, поэтому в вашем примере val автоматически выводится компилятором для аргумента myStringVal.

Какую магию делают классы классов?!

  • Преобразование всех параметров конструктора в public readonly (val) по умолчанию.
  • Создайте методы toString(), equals() и hashcode(), используя все параметры конструктора для каждого метода
  • Создайте сопутствующий объект с тем же именем, содержащий соответствующий метод apply() и unapply(), который в основном представляет собой конструктор удобства, позволяющий создавать экземпляр без использования ключевого слова new и экстрактора, который по умолчанию генерирует опциональную оболочку tuple параметров класса case.

EDIT: Пример вывода компилятора для классов (case) (скопирован из scalatutorial.de)

Простое определение класса 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);
}   

Ответ 2

Scala классы case имеют большое количество шаблонов, реализованных для вас, и наличие всех параметров конструктора, автоматически отображаемых как val, является одной из этих вещей.

Если вы попытаетесь избежать val в обычном классе, например:

trait MyTrait {
  def myVal: String
}

class MyClass(myVal: String) extends MyTrait

Компилятор покажет вам сообщение об ошибке, что MyClass должен быть абстрактным, поскольку он не переопределяет метод myVal, но добавление val или var к параметру конструктора класса решит проблему.

Ответ 3

Классы классов разные - для них создаются некоторые методы по умолчанию. Это включает в себя аттенюаторы для параметров. Подумайте о классах классов как POJO - это полезный бит синтаксического сахара, поскольку им не нужны частные члены.

Также генерируются некоторые другие полезные методы, например copy, toString, apply и unapply.