Классы. Абстрактные классы
Если класс абстрактный - будет базовым классом для других классов. Его экземпляры нельзя будет создавать.
Их цель — предложить реализации функций через наследование подклассам, которые могут иметь экземпляры.
Абстрактный класс объявляется с помощью ключевого слова abstract. Кроме реализованных функций, абстрактные классы включают абстрактные функции — объявления функций без реализации.
Абстрактный класс:
abstract class Worker(val name: String, var age: Int) { abstract fun work() }
Метод work не реализован, но обязателен в классах наследника.
Класс, наследник
class Seller(name: String, age: Int): Worker(name, age) { override fun work() { println("Продаю товар") } }
В данном классе обязательно нужно будет имплементировать метод work.
Операторы:
is - можно проверить является ли объект типом какого-то класса. Если является то в блоке if можем сразу обращаться к этому объекту как к нужному типу.
as - для явного преобразования типа
Пример:
Создаем коллекцию рабочих. Создаем на основе этого списка новый - список уборщиков
val workerCollection = mutableListOf<Worker>() workerCollection.add(Seller("Петя", 25)) workerCollection.add(Seller("Коля", 47)) workerCollection.add(Seller("Саша", 30)) workerCollection.add(Programmer("Маша", 31, "JAVA")) workerCollection.add(Programmer("Даня", 24, "KOTLIN")) workerCollection.add(Programmer("Лев", 50, "C++")) workerCollection.add(Director("Nik", 50)) val cleanerList = workerCollection.filter { it is Cleaner }.map { it as Cleaner } for (worker in cleanerList) { worker.clean() }
Переопределение полей
Мы можем переопределять не только методы, но и поля. Для этого в родительском классе их нужно пометь как open а в дочернем override.
Пример:
Родительский класс
//переменная name объявлена как val - не йнельзя присвоить новое значение, но в классе Car мы переопределили эту переменную и сделали ее var abstract class Vehicle (open val name: String) { abstract fun drive() }
Дочерний класс
class Car(override var name: String = "Машина"): Vehicle(name) { fun startEngine(): Boolean { println("Запускаю движок!") return true } override fun drive() { println("Тачка едет...") } }
Если в main попытаемся поменять имя, то не получится, т.к. экземпляр класса объявлен от родительского. Нужно выполнить проверку.
if (car is Car) { car.name = "Машина 2" }
Умное приведение типов - SmartCast
smartcast - если переменная car не является типом Car, то выходим из метода и остальной код выполняться не будет
if (car !is Car) return car.name = "Машина 2" //У машины есть метод, но вызвать его не можем, т.е. приведен к общему типу - car.startEngine(). // Если добавим условие car is Car && - оно выполнится и будет применено ко второму if (car is Car && car.startEngine())
Анонимные Классы
sportsMan.callWaterBoy(object : WaterBoy { override fun bringWater() { println("Ok") } })
Если нужно один раз передать экземпляр класса - то можно не создавать класс, а создать объект
INLINE
Если всё, что нужно от метода - это вызывать функцию, которую ему передали - нет смысла делать это при помощи создания интерфейса или абстрактных классов.
class SportsMan { inline fun callWaterBoy(bringWater: () -> Unit) { bringWater() } }
Если мы оставим без inline, то будет создан объект анонимного класса у которого будет вызван какой-то метод.
Если указываем inline, то в месте вызова функции передастся данная функция.
sportsMan.callWaterBoy { println("Ok") }
А иначе - вызывали бы анонимный класс вот так
sportsMan.callWaterBoy(object : WaterBoy { override fun bringWater() { println("Ok") } })