Когда используется Abstract Class?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда используется Abstract Class
Определение
Abstract Class (абстрактный класс) — класс, который нельзя инстанцировать напрямую. Он служит шаблоном для наследников, определяя общее поведение и контракт для подклассов.
abstract class Animal {
abstract fun makeSound()
fun sleep() { println("Sleeping...") }
}
// val animal = Animal() // Ошибка компиляции!
val dog = object : Animal() {
override fun makeSound() { println("Woof!") }
}
Когда использовать Abstract Class
1. Общее состояние для иерархии классов
Abstract Class может содержать поля и логику, которые наследуются конкретными классами:
abstract class Vehicle(val maxSpeed: Int) {
protected var currentSpeed = 0
abstract fun accelerate()
open fun brake() {
currentSpeed = 0
}
}
class Car(maxSpeed: Int) : Vehicle(maxSpeed) {
override fun accelerate() {
currentSpeed = minOf(currentSpeed + 10, maxSpeed)
}
}
Здесь все транспортные средства имеют maxSpeed и currentSpeed — это хорошее место для Abstract Class.
2. Приватные/защищённые поля и методы
Abstract Class позволяет инкапсулировать логику через visibility модификаторы:
abstract class Repository<T> {
protected abstract suspend fun fetchFromApi(): T
private fun mapResponse(data: Any): T {
// Внутренняя логика
}
suspend fun get(): T {
return fetchFromApi().let { mapResponse(it) }
}
}
class UserRepository : Repository<List<User>>() {
override suspend fun fetchFromApi(): List<User> {
return apiClient.getUsers()
}
}
Interface не может иметь приватные поля!
3. Конструктор с инициализацией
Abstract Class может иметь конструктор с параметрами, который вызывается при создании подкласса:
abstract class BaseActivity : AppCompatActivity() {
private val logger: Logger
init {
logger = Logger(this::class.qualifiedName)
logger.debug("Activity initialized")
}
}
class MainActivity : BaseActivity() {
// Logger уже инициализирован в BaseActivity
}
4. Иерархия "is-a" отношения
Когда классы являются частью одной семьи:
abstract class Shape {
abstract fun area(): Double
abstract fun perimeter(): Double
}
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
override fun perimeter() = 2 * Math.PI * radius
}
class Rectangle(val width: Double, val height: Double) : Shape() {
override fun area() = width * height
override fun perimeter() = 2 * (width + height)
}
Все это фигуры, они объединены общей природой.
5. Android-специфичные примеры
BaseActivity для повторяемой логики:
abstract class BaseActivity<VM : ViewModel> : AppCompatActivity() {
protected lateinit var viewModel: VM
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(getViewModelClass())
setContentView(getLayoutId())
setupUI()
setupObservers()
}
abstract fun getLayoutId(): Int
abstract fun setupUI()
abstract fun setupObservers()
abstract fun getViewModelClass(): Class<VM>
}
class MainActivity : BaseActivity<MainViewModel>() {
override fun getLayoutId() = R.layout.activity_main
override fun setupUI() { /* UI логика */ }
override fun setupObservers() { /* Observers */ }
override fun getViewModelClass() = MainViewModel::class.java
}
Abstract Class vs Interface
| Аспект | Abstract Class | Interface |
|---|---|---|
| Состояние | Может иметь поля с значениями | Только свойства (Kotlin 1.4+) |
| Конструктор | Да | Нет |
| Видимость | private, protected, public | Только public/internal |
| Наследование | Один класс | Несколько интерфейсов |
| Цель | Иерархия "is-a" | Контракт "can-do" |
Выбирай Abstract Class когда:
- Нужны приватные/защищённые члены
- Есть состояние (поля)
- Нужен конструктор
- Это иерархия одного типа сущностей
Выбирай Interface когда:
- Только контракт методов
- Нет состояния
- Нужна гибкость множественного наследования
- Это "возможность" (Comparable, Serializable)
Практический пример: Android RecyclerView Adapter
abstract class BaseAdapter<T> : RecyclerView.Adapter<BaseAdapter.ViewHolder>() {
protected val items = mutableListOf<T>()
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
abstract inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(item: T)
}
}
class UserAdapter : BaseAdapter<User>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
UserViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false))
inner class UserViewHolder(itemView: View) : ViewHolder(itemView) {
override fun bind(item: User) {
itemView.textView.text = item.name
}
}
}
Итог
Abstract Class — это основа для иерархии классов с общей природой. Используй её когда:
- Нужно шарить состояние между наследниками
- Есть приватные детали реализации
- Нужен конструктор для инициализации
- Это отношение "является" (is-a relationship)
Для простых контрактов используй Interface. Для полноценной иерархии — Abstract Class.