Reputation: 42229
I am building a validation library in Kotlin. The base class is abstract
and implements methods that apply to all types; isNotNull
for example:
abstract class Validator<V>(protected val value: V?) {
fun isNotNull(): Validator<V> {
if(value == null) {
// ... some validation handling here ...
}
return this
}
}
Then I am sub-classing validators for specific types:
class IntValidator(value: Int?) : Validator<Int>(value) {
fun isNotZero(): IntValidator {
if(value == 0) {
// ... some validation handling here ...
}
return this
}
}
Now say I want to validate that an Int? is not null and not zero
val validator = IntValidator(myNullableInteger)
validator
.isNotNull()
.isNotZero()
The code above does not work, because .isNotNull()
returns Validator<V>
, rather than IntValidator
, so .isNotZero()
is no longer in scope.
Is there a way for methods to return the type that instantiated them (in my case, I want it to return IntValidator
, not Validator<T>
)?
Upvotes: 3
Views: 1032
Reputation: 81859
Maybe you should reconsider the API design. What about not chaining the methods and using scope functions instead?
val validator = IntValidator(myNullableInteger)
with(validator) {
isNotNull()
isNotZero()
}
In the scope of an IntValidator
, both methods will be accessible.
Upvotes: 3
Reputation: 3772
As discussed on this kotlinlang thread, you can use extension methods for this:
abstract class Validator<V>(internal val value: V?)
fun <T: Validator<U>, U> T.isNotNull(): T {
if(this.value == null) {
// ... some validation handling here ...
}
return this
}
class IntValidator(value: Int?) : Validator<Int>(value)
@Suppress("FINAL_UPPER_BOUND")
fun <T: IntValidator> T.isNotZero(): T {
if (this.value == 0) {
// ... some validation handling here ...
}
return this
}
fun main() {
val validator = IntValidator(0)
validator
.isNotNull()
.isNotZero()
}
This lets you use the object type as a generic parameter on the function, meaning that you can receive the object type as the result as well. The generic bounds allow you to use type-safety while returning the calling instance's type.
Upvotes: 2
Reputation: 8386
One way, but with an unchecked cast:
fun main(args: Array<String>) {
val validator = IntValidator(2)
validator
.isNotNull()
.isNotZero()
}
abstract class Validator<V, out R>(protected val value: V?) {
open fun isNotNull(): R {
if(value == null) {
// ... some validation handling here ...
}
return this as R
}
}
class IntValidator(value: Int?) : Validator<Int, IntValidator>(value) {
fun isNotZero(): IntValidator {
if(value == 0) {
// ... some validation handling here ...
}
return this
}
}
Upvotes: 2