Reputation: 9142
I'd like to use a kotlin data class as an exception, which seems fine:
data class MyException(val extraData: Any) : RuntimeException()
I'd also like to be able to pass in a cause
to the super class in those cases where one exists. Unfortunately, data classes can only have val
/var
in their primary constructor, and since the default constructor calls the no-args RuntimeException()
constructor, it seems that I simply cannot do this without always requiring cause
to be passed, and stored as a field in my class, which I don't want.
What I want is something like this:
data class MyException(val extraData: Any) : RuntimeException() {
constructor(extraData: Any, cause: Throwable) : this(extraData) super(cause) {}
}
It seems that even if I don't use a data class, I still can't use the convenient var
/val
constructor helpers, since they can only be on a primary constructor which must chose which super constructor to use. The best I can come up with is this, which is quite verbose:
class MyException : RuntimeException {
val extraData: Any
constructor(extraData: Any) {
this.extraData = extraData
}
constructor(extraData: Any, cause: Throwable) : super(cause) {
this.extraData = extraData
}
}
Am I missing something? Is there really no way of conditionally calling a different super-class constructor based on overloaded constructors and still being able to use the var
/val
parameter syntax? If so, why? Are there better ways of doing this sort of thing?
Upvotes: 5
Views: 2316
Reputation: 41510
What you want is perfectly doable with a regular class like this:
class MyException(val extraData: Any, cause: Throwable? = null) : RuntimeException(cause)
In here you have a primary constructor which always takes in extraData
and makes a property out of it. It also takes in the exception cause, but it only passes it into the superclass constructor (note the absence of val
before the second parameter). It also takes advantage of default arguments in Kotlin, which allows you to not specify the cause.
Unfortunately, you can't use a data class specifically in your case for the reason that their primary constructors aren't allowed to have regular arguments. You'd have to declare the cause as a property as well. Data classes are supposed to be used for the simplest cases, and you have a more complex one.
If the superclass needs to be initialized with two different constructors conditionally, then you will have to use two different constructors in your class as well. Any constructor must either delegate superclass initialization to another constructor or do that itself. It can't do both because that would mean that the superclass is initialized twice, which doesn't make sense. Also both superclass initialization and delegation happen before the constructor is executed itself, so you can't have any logic regarding which one to do.
You can't have a primary constructor because it always needs to be delegated to when present. That means that the properties will have to be declared explicitly as well, as the property declaration and initialization syntax only applies to the primary constructor.
Upvotes: 1
Reputation: 30676
Since both this(...)
& super(...)
is the first statement in a constructor, so you can't call it simultaneously. otherwise, you will obtain a compile-time error.
IF any classes contains a primary constructor, their secondary constructors must call its primary constructor explictly, so you can't call additional super(...)
on secondary constructor at all.
if the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the
this
keyword
But there is another way to set the cause by Throwable#initCause, for example:
data class MyException(val extraData: Any) : RuntimeException() {
constructor(extraData: Any, cause: Throwable) : this(extraData) {
initCause(cause)
}
}
AND the data class is designed for POJO rather than Exception
s.
Upvotes: 3