I have a `sealed class MyError`. in my unit tests,...
# getting-started
y
I have a
sealed class MyError
. in my unit tests, I have code that checks a list of `MyError`s produced by some other source. for some error, I'd like to assert that the error is an instance is of a specific subclass, say
MyError.Foo
Copy code
data class AcceptsSpecificError {
  fun check(e: MyError) {
    // assert that `e` has type `MyError.Foo`
  }
}
how can I do this? I can't make this
AcceptsSpecificError<T: MyError>
because of type erasure. also it's not feasible for
AcceptsSpecificError
to take a
val e: MyError
and then compare to that, because some of the subclasses of
MyError
are too complex to easily instantiate in a unit test environment. EDIT: would taking a property of type
KClass<T: MyError>
work here? what I really want here is a "phantom data" kind of value I can take so that
T
is not type erased.
j
Does this not work?
assertTrue(e is MyError.Foo)
y
it does, but the subclass is meant to be a generic here. intended usage is something like
AcceptsSpecificError<Foo.Bar>().check(e)
. I got this to work by adding a property of type
KClass
. which, after searching around, seems to be what I'm supposed to do here.
d
Copy code
import kotlin.reflect.KClass

sealed class MyError {
    data object Foo : MyError()
    data object Bar : MyError()
}

class AcceptsSpecificError<T : MyError>(val type: KClass<T>) {
    fun check(e: MyError) {
        check(type.isInstance(e)) { "Expected $type, got ${e::class}" }
    }
}

inline fun <reified T : MyError> AcceptsSpecificError() = AcceptsSpecificError(T::class)
1
y
@Daniel Pitts woah, that was so simple. thank you
👍 1
too bad you can't completely hide away the implementation detail here, because
inline fun
is not allowed to access a non-public constructor
d
You can, but there really isn't a benefit to do so.
You can change the signature here:
Copy code
class AcceptsSpecificError<T : MyError> @PublishedApi internal constructor(val type: KClass<T>) {
This makes the constructor "internal" as far as Kotlin is concerned, but effectively "public" to the JVM. This allows it to be called from an inline function, while maintaining a little bit of implementation hiding.
I don't think I'd go that far though. Users might legitimately want to pass in a
KClass<T>
themselves, even if they aren't in a context where they have a reified type
T
.
y
thanks again, I appreciate the explanation.
d
You're welcome!