Классы и объекты в Scala | OTUS

Классы и объекты в Scala

Если вы программировали на языке Java, то многие вещи на Scala относительно ООП будут вам знакомы. Для объявления класса используется ключевое слово class, новый экземпляр объявляется через new. Методы класса являются функциями, объявленными в его теле, а поля класса указываются сразу же после имени в качестве списка аргументов. Однако по умолчанию они объявляются как private val, поэтому, если мы не укажем никаких модификаторов, указанное поле будет доступно лишь внутри класса и будет неизменяемым. Класс в Scala можно сделать абстрактным, если добавить abstract перед объявлением. Но главное отличие от Java — отсутствие конструктора. Код, который должен выполняться во время создания объекта, пишется непосредственно в теле класса.

Рассмотрим пример использования класса в Scala:

class Foo(x: Int) {
  def bar(y: Int) = x + y
}

val foo = new Foo(1)
// foo: Foo = Foo@69007238

foo.bar(1)
// res4: Int = 2

А теперь перед нами аналогичный код, но уже на Java:

class Foo {
  private int x_;

  Foo(int x) {
    _x = x;
  }

  public int bar(int y) {
    return x + y;
  }
}

Foo foo = new Foo(1)

int result = foo.bar(1)
// result теперь равен 2

Обратите внимание, что в Scala public указывать не обязательно, и аргументы конструктора доступны во всем классе, а локальное приватное поле создавать тоже не обязательно. Вдобавок к этому, в Scala можно сразу объявить объект без создания класса, используя ключевое слово object. В результате реализуется паттерн Singleton.

object Singleton(field: String) {
  def squareMe(x: Int) = x*x
}

Singleton.squareMe(2)
// res5: Int = 4

А вот аналогичный код на Java будет более объёмным:

public class Singleton {
  private static Singleton instance = null;

  protected Singleton() {
    // Exists only to defeat instantiation.
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }

  public int squareMe(int x) {
    return x*x;
  }
}

int a = Singleton.getInstance().squareMe(2)
// a теперь равен 4

В нашем примере мы пометили конструктор как protected, дабы исключить возможность его вызова извне, то есть обращение к объекту будет выполняться с помощью метода getInstance(), который при первом вызове инициализирует экземпляр класс, а в последующем возвратит уже созданный экземпляр. Также допускается существование объекта и класса с тем же именем, причём они делят область видимости. В результате необходимость в директиве static отпадает — методы, которые объявлены не в классе, а в объекте, ведут себя, как статические. Этот объект называется согласно терминологии Scala «companion object».

Но вернёмся к конструкторам и вспомним, что в случае использования любого объекта к некоторым аргументам по дефолту вызывается метод apply. Воспользуемся этим и напишем класс с несколькими статическими методами, конструкторами, неизменяемыми и изменяемыми полями в идиоматичном для Scala стиле, а потом продублируем тот же код, но уже на Java.

Итак, вариант Scala:

object MyUselessClass {
  def staticMethod(x: Int) = x + 5

  def apply(immutableField: Int): MyUselessClass = new MyUselessClass(immutableField, 2)

  def apply(immutableField: Int, mutableField: Int): MyUselessClass = new MyUselessClass(immutableField, mutableField)

  def apply(immutableField: Int, mutableField: Int, privateField: Int): MyUselessClass = new MyUselessClass(immutableField, mutableField, privateField)
}

class MyUselessClass(val immutableField: Int, var mutableField: Int, privateField: Int = 8 /*значение по умолчанию*/) {
  def instanceMethod() = {
    val sumOfFields = immutableField + mutableField + privateField
    MyUselessClass.staticMethod(sumOfFields)
  }
}

// 1-й конструктор, обратите внимание на отсутствие 'new',
// ведь на самом деле это вызов метода 'apply'
val myUselessObject = MyUselessClass(1)

// как и в предыдущем варианте
val myAnotherUselessObject = MyUselessClass.apply(1)

// 3-й конструктор
val myThirdUselessObject = MyUselessClass(1, 2, 3)

// Вызываем метод
myUselessObject.instanceMethod()
// res6: Int = 16

// Поля доступны так же, как методы
myUselessObject.mutableField
// res7: Int = 2
myUselessObject.immutableField
// res8: Int = 1
myUselessObject.mutableField = 9
myUselessObject.mutableField
// res9: Int = 9

// Вызываем статический метод
MyUselessClass.staticMethod(3)
// res10: Int = 8

А теперь смотрим Java-вариант:

public class MyUselessClass {
  private int immutableField_;
  private int mutableField_;
  private int privateField_ = 8;

  MyUselessClass(int immutableField) {
    immutableField_ = immutableField;
    mutableField_ = 2;
  }

  MyUselessClass(int immutableField, int mutableField) {
    immutableField_ = immutableField;
    mutableField_ = mutableField;
  }

  MyUselessClass(int immutableField, int immutableField, int privateField) {
    immutableField_ = immutableField;
    mutableField_ = mutableField;
    privateField_ = privateField;
  }

  int getImmutableField() {
    return immutableField;
  }

  int getMutableField() {
    return mutableField;
  }

  void setMutableField(int newValue) {
    mutableField = newValue;
  }

  public static int staticMethod(int x) {
    return x + 5;
  }

  public int instanceMethod() {
    int sumOfFields = immutableField + mutableField + privateField;
    return staticMethod(sumOfFields);
  }
}

// 1-й конструктор
MyUselessClass myUselessObject = new MyUselessClass(1)

// 3-й конструктор
MyUselessClass myAnotherUselessObject = new MyUselessClass(1, 2, 3)

// Вызываем метод
myUselessObject.instanceMethod()
// возвратит 16

// Поля доступны так же, как методы
myUselessObject.getMutableField
// возвратит 2

myUselessObject.getImmutableField
// возвратит 1

myUselessObject.setMutableField(9)
myUselessObject.getMmutableField
// возвратит 9

// Вызываем статический метод
MyUselessClass.staticMethod(3)
// возвратит 8

Материал подготовлен на основании статьи "Быстрый старт со Scala".

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто