Как стать автором
Обновить

Комментарии 11

Приём с наследником sealed класса в java — интересный.
Но конкретно для option можно вместо Option(null), который вернёт None, вызвать Some(null) и получить примерно те же эффекты.

Спасибо, проверил на тех же тестах — MatchError, естественно, не получил, но NPE — есть и contains(null) === true — есть. Честно говоря, немного не ожидал, почему-то думал, что в этом случае код бросит ошибку при вызове конструктора.

Я, наверное, чего-то не понимаю, но почему sealed-класс в Scala не является final-классом в Java? Из-за этого ведь все проблемы.

А в этом случае JVM разве не ругнётся на то, что от него кто-то отнаследовался в Scala?

А разве в Scala можно отнаследоваться от Option? Статья же говорит, что нет, ведь он sealed.

Извне — нельзя, но внутри того же пакета у него есть наследники — case class Some и case object None. Я пытаюсь понять, если при компиляции Scala объявит Option как final, примет ли JVM наличие Some и None?

Ах, вот оно в чём дело… Я со Scala'ой не знаком, потому мне и непонятно было, почему sealed-классы не былы сделаны final'ьными. Но раз есть (приватные?) наследники, выходит, по-другому сделать было нельзя.

Там как раз в том и суть, да, что они есть (и даже не приватные). С точки зрения Scala, sealed-класс — это такой, все наследники которого известны во время компиляции: поэтому он, собственно, и может предупредить о потенциальном MatchError.

Что интересно, sealed классы в kotlin'e таким образом не сломаешь — конструктор у базового класса помечен как synthetic, что делает невозможным его вызов из Java (да и других jvm языков) без применения особой уличной магии.
Почему в скале не сделали так-же — непонятно, вполне очевидная штука.


kotlin
sealed class Option {
    class Some<out T>(val value: T) : Option()
    object None : Option()
}

java
import kotlin.jvm.internal.DefaultConstructorMarker;

public abstract class Option {
    private Option() {
    }

    public /* synthetic */ Option(DefaultConstructorMarker $constructor_marker) {
        this();
    }

    public static final class Some<T>
    extends Option {
        private final T value;

        public final T getValue() {
            return this.value;
        }

        public Some(T value) {
            super(null);
            this.value = value;
        }
    }

    public static final class None
    extends Option {
        public static final None INSTANCE;

        private None() {
            super(null);
        }

        static {
            None none;
            INSTANCE = none = new None();
        }
    }

}

Котлин более свежий язык. Я думаю, что они как раз учли ошибки других JVM языков. Ну по крайней мере постарались. И в Котлин версии уже хаки грязные не проходят. Хотя конечно сложно представить, что кто-то будет такой ад пытаться сделать в продакшен коде.

Это не меняет ситуации с данной конкретной проблемой. Разработчики jvm языков вполне осведомлены о наличии synthetic, который был с лохматых времён (в 1.5 точно был, возможно даже раньше, если не с 1.0), и активно им пользуются в других местах.
Добавление synthetic не ломает abi (разве что api для случаев, описанных в статье, но кого волнует совместимость с теми, кто в обход скалы пытается отнаследоваться от sealed?), его вполне можно добавить хоть прямо сейчас в компилятор.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации