Свойства конструктора Котлина и вызов различных конструкторов суперкласса

Я хотел бы использовать класс данных kotlin как исключение, которое кажется прекрасным:

data class MyException(val extraData: Any) : RuntimeException() 

Я также хотел бы иметь возможность передать cause супер-классу в тех случаях, когда он существует. К сожалению, классы данных могут содержать только val / var в своем основном конструкторе, и поскольку конструктор по умолчанию вызывает конструктор no-args RuntimeException() , кажется, что я просто не могу сделать это, не требуя при этом, чтобы cause была передана и хранилась как поле в моем классе, которого я не хочу.

Я хочу что-то вроде этого:

 data class MyException(val extraData: Any) : RuntimeException() { constructor(extraData: Any, cause: Throwable) : this(extraData) super(cause) {} } 

Кажется, что даже если я не использую класс данных, я все еще не могу использовать удобные помощники конструктора var / val , так как они могут быть только в основном конструкторе, который должен выбрать, какой супер конструктор использовать. Самое лучшее, что я могу придумать, это то, что довольно много:

 class MyException : RuntimeException { val extraData: Any constructor(extraData: Any) { this.extraData = extraData } constructor(extraData: Any, cause: Throwable) : super(cause) { this.extraData = extraData } } 

Я что-то упускаю? Не существует ли способа условного вызова другого конструктора суперкласса на основе перегруженных конструкторов и все еще может использовать синтаксис параметров var / val ? Если да, то почему? Есть ли лучшие способы сделать такие вещи?

То, что вы хотите, отлично справляется с обычным классом:

 class MyException(val extraData: Any, cause: Throwable? = null) : RuntimeException(cause) 

Здесь у вас есть первичный конструктор, который всегда берет в extraData и делает из него свойство. Он также принимает причину исключения, но только передает ее в конструктор суперкласса (обратите внимание на отсутствие val перед вторым параметром). Он также использует аргументы по умолчанию в Kotlin, что позволяет не указывать причину.

К сожалению, вы не можете использовать класс данных специально в своем случае по той причине, что их основным конструкторам не разрешено иметь регулярные аргументы. Вы должны также объявить причину как свойство. Класс данных предполагается использовать для простейших случаев, и у вас более сложный.

Если суперкласс должен быть инициализирован двумя разными конструкторами условно, тогда вам придется использовать два разных конструктора в своем классе. Любой конструктор должен либо делегировать инициализацию суперкласса другому конструктору, либо сделать это сам. Он не может сделать так, потому что это означает, что суперкласс инициализируется дважды, что не имеет смысла. Также как инициализация суперкласса, так и делегирование происходят до того, как конструктор выполняется сам, поэтому вы не можете иметь никакой логики относительно того, что делать.

У вас не может быть первичного конструктора, потому что он всегда должен быть делегирован при наличии. Это означает, что свойства должны быть явно объявлены, так как синтаксис объявления свойства и инициализации применим только к основному конструктору.

Поскольку и this(...) & super(...) – это первый оператор в конструкторе, поэтому вы не можете его одновременно вызвать. в противном случае вы получите ошибку времени компиляции.

Если любые классы содержат первичный конструктор , их вторичные конструкторы должны явно вызывать его основной конструктор, поэтому вы не можете вообще вызвать дополнительный super(...) на вторичный конструктор.

если класс имеет первичный конструктор, каждый вторичный конструктор должен делегировать основному конструктору, прямо или косвенно, через другой вторичный конструктор (ы). Делегирование другого конструктора того же класса выполняется с использованием this ключевого слова

Но есть еще один способ установить причину Throwable # initCause , например:

 data class MyException(val extraData: Any) : RuntimeException() { constructor(extraData: Any, cause: Throwable) : this(extraData) { initCause(cause) } } 

И класс данных предназначен для POJO, а не для Exception .