Я начинаю изучать программирование Scala и программирование ООП. Я не понимаю понятие абстрактного класса. Я прочитал этот пример:
Рассмотрим задачу написания класса для множеств целых чисел с двумя операциями, включая и содержит. (s с x) должен возвращать новый набор, содержащий элемент x вместе со всеми элементами множества s. (s содержит x) должен возвращать true, если множество s содержит элемент x и в противном случае должно возвращать false. Интерфейс таких множеств задается:
abstract class IntSet {
def incl(x: Int): IntSet
def contains(x: Int): Boolean
}
Допустим, мы планируем реализовать наборы как двоичные деревья. Есть две возможные формы деревьев. Дерево для пустого множества и дерево, состоящее из целого и двух поддеревьев. Вот их реализации.
class EmptySet extends IntSet {
def contains(x: Int): Boolean = false
def incl(x: Int): IntSet = new NonEmptySet(x, new EmptySet, new EmptySet)
}
class NonEmptySet(elem: Int, left: IntSet, right: IntSet) extends IntSet {
def contains(x: Int): Boolean =
if (x < elem) left contains x
else if (x > elem) right contains x else true
def incl(x: Int): IntSet =
if (x < elem) new NonEmptySet(elem, left incl x, right)
else if (x > elem) new NonEmptySet(elem, left, right incl x)
else this
}
И EmptySet, и NonEmptySet расширяют класс IntSet. Это означает, что типы EmptySet и NonEmptySet соответствуют типу IntSet - значение типа EmptySet или NonEmptySet может использоваться везде, где требуется значение типа IntSet.
Мне непонятно, почему полезно вводить анонимный класс, поскольку в двух классах, которые расширяют анонимный, снова появляется определение вставки и содержит.
Благодарю!
Основным преимуществом использования InClass
(который является абстрактным классом, а не анонимным классом) является то, что он определяет тип элемента, экземпляры которого всегда должны реализовывать incl
и contains
что позволяет вам абстрагировать детали реализации при управлении, в вашем случае, установить объекты.
Одним из примеров того, как это здорово, является то, что вы можете иметь список наборов Empty
и NomEmpty
вместе и использовать их нечетко, используя контракт, заданный вашим абстрактным классом:
val l = List[IntSet](new EmptySet, new NomEmptySet(1, new EmptySet, new EmptySet)
val setsContainingThree = l.filter(_.contains(3))
Ваш код использует это, обратите внимание, что я написал new NomEmptySet(1, new EmptySet, new EmptySet)
но он также может быть:
new NomEmptySet(1, new EmptySet, new NomEmptySet(2, new EmptySet, new EmptySet))
NonEmptySet
ожидает InSet
поэтому ваши фактические параметры могут быть любыми типами, которые являются InSet
, то есть: EmptySet
или NonEmptySet
.
Это не Scala или Java, а основной принцип ООП.
Класс не анонимный, он абстрактный. Какой -in простой words- означает, что вы не можете его создать. Однако вы можете использовать его, например, в сигнатуре метода... вы никогда не узнаете, какая реализация передана вашему методу, но оба предлагают один и тот же интерфейс для взаимодействия с ними (поскольку оба являются IntSet
s).
Если это помогает, вы можете думать об абстрактном классе как о интерфейсе с реализациями метода или как о простой форме признака.