Подтвердить что ты не робот

Расширяет ли класс в scala с параметрами конструктора добавляет vals (поля) в класс?

Скажем, у меня есть:

class A(val foo: String)

class B(foo: String) extends A(foo)
class C(val foo: String) extends A(foo)
class D(override val foo: String) extends A(foo)
class E(bar: String) extends A(bar)

Мне интересно, сколько экземпляров памяти каждого из этих классов заняло. Экземпляры класса A будут иметь одну переменную-член: foo.

Как насчет классов B, C, D и E? Сколько переменных-членов они будут иметь? Я подозреваю, что у E будет два (E.bar, A.foo), я ожидаю, что у D будет один (A.foo), но мне интересно, о B и C, возможно, у них есть два? (B.foo, A.foo)?

4b9b3361

Ответ 1

Все примеры, которые компилируются (A, B, D, E), занимают ровно столько же места для хранения. На самом деле даже

class F(val bar: String) extends A(bar)

будут храниться данные в одном поле - он просто получает дополнительный метод доступа для одного и того же поля. Однако, если вы

class G(var bar: String) extends A(bar)

то построено новое поле.

Вы можете проверить все это, скомпилировав приведенные выше примеры и посмотрев на байт-код из javap -c Classname (обратите внимание на putfield at 2: в конструкторе A):

public class Sizes$A extends java.lang.Object implements scala.ScalaObject{
public java.lang.String foo();
  Code:
   0:   aload_0
   1:   getfield    #11; //Field foo:Ljava/lang/String;
   4:   areturn

public Sizes$A(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #11; //Field foo:Ljava/lang/String;
   5:   aload_0
   6:   invokespecial   #18; //Method java/lang/Object."<init>":()V
   9:   return    
}

(И отсутствие дополнительного putfield в F...)

public Sizes$F(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokespecial   #15; //Method Sizes$A."<init>":(Ljava/lang/String;)V
   5:   return

(И наличие одного снова в G...)

public Sizes$G(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #11; //Field bar:Ljava/lang/String;
   5:   aload_0
   6:   aload_1
   7:   invokespecial   #18; //Method Sizes$A."<init>":(Ljava/lang/String;)V
   10:  return

Ответ 2

Для класса C для <компиляции > требуется <override ключевое слово override, что делает его идентичным D. Он сохранит свою собственную копию foo. Класс B не добавляет хранилище, если где-то вне его тела конструктора есть ссылка на foo. Это заставило бы скрытое поле быть создано, чтобы удерживать параметр конструктора. Тело конструктора - это весь код внутри определения класса и вне любого тела метода.

Приложение:

package storage

    class A(val foo: String)

    class B(             foo: String) extends A(foo)
//  class C(         val foo: String) extends A(foo)
    class D(override val foo: String) extends A(foo)
    class E(             bar: String) extends A(bar)
    class F(             bar: String) extends A(bar) { def barbar: String = bar }

Я озадачен этим:

% javap -private storage.F
Compiled from "Storage.scala"
public class storage.F extends storage.A implements scala.ScalaObject{
    public java.lang.String barbar();
    public storage.F(java.lang.String);
}

Что используется метод barbar для получения его возвращаемого значения?