Концепции объектно-ориентированного программирования и способы их реализации в Scala
Scala — это популярный язык программирования, функциональный и объектно-ориентированный, работающий на виртуальной машине Java (JVM). Шаблон объектно-ориентированного программирования (ООП) был разработан для преодоления недостатков, присущих другим методологиям программирования. Этот новый ООП-подход произвел новую революцию в технологиях и языках программирования. ООП позволяет нам писать программы с классами и объектами с учетом важности данных. Так что состояние и поведение этих объектов будут максимально похожи на сущностей реального мира.
Давайте раскроем объектно-ориентированные концепции на примерах:
1. Классы и объекты
1.1 Определение классов
В Scala мы определяем класс с помощью ключевого слова class. Основная функция класса — хранить данные, информацию или состояние. Мы можем создать объект класса с ключевым словом new.
class Person val person = new Person
1.2 Определение полей
Мы можем определить поля в классе, используя var или val, как определение переменной.
val
создает неизменяемую переменную (например,final
в Java)var
создает изменяемую переменную
class Person { val name: String = "John" var age: Int = 26 }
Мы можем получить доступ к полям, создав объект.
val person = new Person println(person.age) println(person.name)
1.3 Определение методов
Мы можем определить метод, используя defключевое слово. Не нужно указывать ключевое слово return, так как возвращаемое значение является результатом последней инструкции в методе.
class Person { val name: String = "John" var age: Int = 26 def get_info(): Unit = println(s"Age of $name is $age") }
Мы можем вызвать метод после того, как создадим объект для класса.
val person = new Person person.get_info()
1.4 Конструкторы
Первичный конструктор используется для присвоения значений в классе, которые будут использоваться позже в полях или методах класса.
class Person(val name: String, val age: Int) { def get_info(): Unit = println(s"Age of $name is $age") } val person = new Person("John", 26) println(person.age)
name и age являются параметрами класса, а не полями класса, что означает, что вы не можете получить доступ к этим параметрам через объекты класса. Единственный способ преобразовать параметр класса в поле класса — добавить val / var перед параметр.
2. Инкапсуляция
Инкапсуляция является фундаментальной концепцией ООП, она описывает идею объединения данных и методов, которые работают с этими данными внутри класса.
class Person( private val name: String, private val age: Int) { def info(): Unit = println(name +" "+ age) } val person = new Person("John", 26) person.info()
В определении класса переменные являются закрытыми, и мы не можем получить к ним доступ из созданного нами объекта. Это приведет к ошибке компиляции. Мы можем получить к ним доступ или манипулировать ими с помощью общедоступных методов, созданных внутри класса.
3. Наследование
Scala имеет наследование на уровне одного класса, как и другие языки (т.е. вы можете наследовать только один класс за раз). Класс может наследовать другой класс, используя ключевое слово extends.
class Animal { def eat(): Unit = println("nomnom") } class Cat extends Animal val cat = new Cat cat.eat()
В приведенном выше коде Animal — это суперкласс, а Cat — подкласс. Подкласс может наследовать поля и методы от суперкласса.
class Animal { def eat(): Unit = println("nomnom") private def walk(): Unit = println("I am walking") protected def run(): Unit = println("I am running") }
В приведенном выше примере:
- walk() определен как частный метод — доступ к закрытым методам возможен только из класса, в котором метод определен.
- run() определен как защищенный метод — защищенные методы могут быть доступны классу, в котором метод определен, и подклассу.
class Cat extends Animal { def catRun(): Unit = { // Works fine as run is a protected method in super class run() } } val cat = new Cat cat.catRun()
Мы можем использовать ключевое слово final или sealed, чтобы предотвратить наследование. Мы можем назначить это ключевое слово перед любым классом.
final class Vehicle // Compilation Error: class Car cannot extend final class Vehicle class Car extends Vehicle
Использование ключевого слова sealed для предотвращения наследования. Классы в одном файле могут наследовать суперкласс, но классы, присутствующие в любом другом файле, не могут наследовать суперкласс. Это может быть полезно, когда вы хотите ограничить количество классов, наследуемых от суперкласса. Предположим, если вы считаете, что Cat и Dog могут быть только двумя подклассами Animal, определите Animal как запечатанное и определите классы Cat и Dog в одном и том же файле.
sealed class Employee class Manager extends Employee
4. Полиморфизм
Полиморфизм — это способность языка ООП обрабатывать данные по-разному в зависимости от их типов входных данных.
4.1 Перегрузка методов
Перегрузка метода – это распространенный способ реализации полиморфизма. Методы могут иметь одно и то же имя, но иметь разный список параметров (т. е. количество параметров, порядок параметров и типы данных параметров).
class Person(var name: String, val age: Int) { def greet(name: String): Unit = { println(s"${this.name} says, Hi $name") } def greet(): Unit = { println(s"Hi, I am $name") } } object MethodOverloading extends App { val person = new Person("John", 26) person.greet("Daniel") // John says, Hi Daniel person.greet() // Hi, I am John }
- Компилятор не учитывает тип возвращаемого значения при дифференциации перегруженного метода.
- Вы не можете объявить два метода с одинаковой сигнатурой и разными типами возвращаемого значения. Это вызовет ошибку времени компиляции.
4.2 Переопределение метода
Переопределение метода — это еще один способ реализацииполиморфизма. Если подкласс имеет имя метода, идентичное имени метода, определенному в родительском классе, то известно, что это Переопределение метода. Метод должен иметь то же имя и параметры, что и в родительском классе.Когда подкласс хочет передать конкретную реализацию для метода, определенного в родительском классе, тогда этот подкласс переопределяет определенный метод. из родительского класса.
Предположим, у нас есть класс Animal с полем creatureType и методом eat, как показано ниже.
class Animal { val creatureType = "Wild" def eat(): Unit = println("nomnom") }
Теперь есть еще один класс Dog, который наследует класс Animal, но имеет другую реализацию для поля creatureType и eat. метод.
class Dog extends Animal { override val creatureType = "Domestic" override def eat(): Unit = println("Crunch, Crunch") }
Теперь давайте создадим экземпляр этих классов и будем использовать эти члены.
object MethodOverriding extends App { val animal: Animal = new Animal animal.eat() // nomnom println(animal.creatureType) // Wild val dog: Animal = new Dog dog.eat() // Crunch, Crunch println(dog.creatureType) // Domestic }
- Для переопределения методов одним из важнейших правил является то, что класс, который переопределяет, должен использовать модификатор override.
- В переопределении метода мы не сможем переопределить var с помощью val или наоборот, иначе будет выброшена ошибка.
Спасибо за просмотр этого блога.