← Назад к вопросам

Когда используется Abstract Class?

1.6 Junior🔥 181 комментариев
#Kotlin основы#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда используется 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 ClassInterface
СостояниеМожет иметь поля с значениямиТолько свойства (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.

Когда используется Abstract Class? | PrepBro