I started playing with the new context receivers feature. I intend to use that as a "localized dependency injection" to pass client context around. Currently, I have this (ClientProvider is a fun interface):
fun <T> withClient(client: Client, block: ClientProvider.() -> T) =
ClientProvider { client }.block()
This works pretty well in the production code, e.g. I can do
class MyService {
context(ClientProvider)
fun methodUsingClient() {}
}
However, an issue arises when I want to mockk this code in tests. Currently, I have
val myService: MyService = mockk { every { methodUsingClient() } just runs }
This obviously doesn't work because the ClientProvider context is missing. I would like to fix that by composing mockk and withClient. I imagine it could look like this
inline fun <reified T : Any> mockkWithClient(noinline block: context(T) ClientProvider.() -> Unit) =
mockk<T> { withClient(mockk(), block) }
This indeed works at the call site, i.e., the compiler seems to be happy with
val myService: MyService = mockkWithClient { every { methodUsingClient() } just runs }
but the function itself doesn't compile - the compiler complains about the block parameter:
Type mismatch.
Required:
ClientProvider.() → TypeVariable(T)
Found:
context(T) ClientProvider.() → Unit
Intuitively, I would expect that the extra T context wouldn't harm the block usage inside withClient but apparently, it does.
Is there any way how I can define mockkWithClient so it can be used as outlined above?
I got a useful answer to my other, more generally formulated question, and based on that I was able to solve this problem as well (the key point I was missing is that I have to manually pass the receivers to the block):
inline fun <reified T : Any> mockkWithClient(noinline block: context(ClientProvider) T.() -> Unit) =
withMockClient<T> { mockk { block(this#withMockClient, this#mockk) } }
fun <T> withMockClient(block: ClientProvider.() -> T) =
ClientProvider { mockk() }.block()
The intended usage is then as expected:
mockkWithClient { every { methodUsingClient() }
Note, however, the very specific type of block: it's context(ClientProvider) T.() -> Unit. If I read the documentation correctly I should be also able to write context(ClientProvider, T) () -> Unit but that doesn't compile with the message Subtyping relation between context receivers is prohibited. The root cause of this is still unknown to me but my original problem is solved, nevertheless.
Related
I am trying to create a dynamic service based on the data class model defined by user and they registerDataModels() method appDataModule() it should automatically create all based method in the router service. When I try to achive using generics in those method I am getting a compiler error. Is there any other better way to dynamically create route methods like by defining the datamodel by developer and then service should be automatically created?
org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
File being compiled: */api/AppConfigService.kt
The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
File is unknown
The root cause java.lang.AssertionError was thrown at: org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor.spillVariables(CoroutineTransformerMethodVisitor.kt:636)
fun Application.registerDataModels() {
appDataModule<M1>()
appDataModule<M2>()
appDataModule<M3>()
}
inline fun <reified T: DBModel> Application.appDataModule() {
routing {
createAppData<T>()
updateAppData<T>()
deleteAppData<T>()
}
}
inline fun <reified T: DBModel> Route.createAppData() {
put("/api/data/${getName<T>()}/create") {
authenticated {
create<T>{}
}
}
}
inline fun <reified T: DBModel> Route.updateAppData() {
put("/api/data/${getName<T>()}/update") {
authenticated {
update<T>{}
}
}
}
inline fun <reified T: DBModel> Route.deleteAppData() {
put("/api/data/${getName<T>()}/delete") {
authenticated {
delete<T>{}
}
}
}
Note: This answer assumed that code would be loaded at runtime, which seems not to be the case, and is therefore not completely matching OP's question.
You are using inline functions with reified.
To make a long story short, inline functions are compiled and 'copied' to the location where they are being used, already with a fixed (thats what reified does) class. So when you use an inline function
inline fun <reified T> foo(t: T): T { ... }
and you call it like this:
val myVal = foo("test").uppercase()
then at compile time of that calling line of code, the type of T is known to be String and the target line is compiled accordingly, so you know at runtime which type T is within your function.
It is (for this one calling line) as if that function was like this to begin with:
fun foo(t: String): String { ... }
Because you want to compile these classes dynamically, however, this process fails, because the class obviously does not exist yet. This is simply due to the nature of reified. If you can somehow remove it, it might work.
I agree that the error message of the compiler could be more telling here. Maybe you can raise a task on kotlin's issue tracking platform?: https://youtrack.jetbrains.com/issues/kt?_gl=1*5r6x4d*_ga*MTQyMDYxMjc2MS4xNjMzMzQwMzk5*_ga_9J976DJZ68*MTY2OTM1NjM1MS4yMS4xLjE2NjkzNTYzNTcuMC4wLjA.&_ga=2.265829455.1332696793.1669356352-1420612761.1633340399
I am trying to declare two suspend methods with list of String and PublishRequest Object as parameter. But the IDE is giving error with this.
The error is either make one of the function internal or remove suspend. But i want to use coroutines inside both of them.
override suspend fun publish(publishRequests: List<PublishRequest>) {
///code
}
suspend fun publish(events: List<String>) {
///code
}
The PublishRequest Data class is internal. The issues is only coming when we add the publish(events: List) method. The code is working fine the publish(publishRequests: List)
Can you explain why it is happening ?
The problem you are facing is related to type erasure.
The types List<PublishRequest> and List<String> are erased to List<*>, as consequence, you would have a JVM signature clash.
To solve your problem you have two different solutions.
Change their names and avoid a signature clash:
suspend fun publishRequests(publishRequests: List<PublishRequest>) {}
suspend fun publishEvents(events: List<String>) {}
Use a single function with a reified type and handle the different type classes inside that function:
suspend inline fun <reified T> publish(objects: List<T>) {
when {
PublishRequest::class.java.isAssignableFrom(T::class.java) -> // it's a list of PublishRequest
T::class == String::class -> // it's a list of String
}
}
I'm trying to write an extension function for Observable.subscribe which automatically logs errors.
fun <T> Observable<T>.subscribeAndLogE(onNext: Consumer<in T>): Disposable =
subscribe(onNext, ErrorConsumer())
The ErrorConsumer works and presumably logs the error, but subscribeAndLogE does not accept lambdas like .subscribe() does.
observer.subscribe {
//works
}
observer.subscribeAndLogE {
//does not work
}
It says:
With that OnboardingScreen being whichever value T would normally be.
I don't see the original Consumer<in T> in Observable doing anything special to accept lambdas. What am I doing wrong here?
You are passing a parameter of type Consumer to the function. You need to pass a function for the lambda syntax to work. This would work the way you want to:
fun <T> Observable<T>.subscribeAndLogE(onNext: (it : T) -> Unit): Disposable =
subscribe({ onNext(it) },{ throwable -> Log(throwable) })
and use it like so:
observer.subscribeAndLogE {
//works
}
I'm trying to keep this minimal, but let me know if I'm being too minimal.
Suppose you have a class hierarchy like this, designed for generating HTML (inspired by the Kotlin tutorial; semi-pseudocode follows):
class Tag {
protected val children = arrayListOf<Tag>()
operator fun String.unaryPlus() = children.add(Text(this))
}
class TagWithChildren : Tag() {
fun head(init: Head.() -> Unit) = initializeTag(Head(), init)
fun script(init: Script.() -> Unit) = initializeTag(Script(), init)
fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
}
class Head : TagWithChildren()
class Script : Tag()
class Text(val str: Text) : Tag()
Notice that Head has head and script methods while Script doesn't.
Now you can construct a template that looks like this:
head {
script {
+"alert('hi');"
}
}
Which works great! However, if the block passed to script tries to call methods that aren't available on Script, it can call the method on Head instead. For example,
head {
script {
script {
+"alert('hi');"
}
}
}
not only isn't a compile error, it's actually equivalent to
head {
script {
}
script {
+"alert('hi');"
}
}
which is super confusing, from a template author's perspective.
Is there any way to prevent method lookups from traveling up the scope like that? I only want it to look at the innermost scope.
UPDATE 11/24/2016:
Kotlin 1.1-M03 has introduced scope control, which I believe solves exactly this problem. https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/
The current behavior is intentional. Code in a lambda has access to receivers of all enclosing scopes. It is possible that a future version of Kotlin will add a modifier that will restrict a lambda with receiver to calling methods on that receiver only and not the enclosing scopes, but in the current version there's no way to change that behavior.
As a workaround, I can have it fail at runtime if I change the classes to look like this:
open class Tag {
operator fun String.unaryPlus()
// pulled up from TagWithChildren, call protected method
fun head(init: Head.() -> Unit) = addChild(Head())
fun script(init: Script.() -> Unit) = addChild(Head())
// throws in Tag
open protected fun addChild(t: Tag) = throw IllegalArgumentException()
}
class TagWithChildren : Tag() {
// overridden to not throw in subclass
protected override fun addChild(t: Tag) = children.add(t)
}
This way, every Tag has the builder methods (solving the scoping problem), but actually calling them may result in a runtime failure.
I have class:
class Generic<T : SuperType>() { ... }
And this code is't correct, but cast s to type T:
fun typeCheck(s: SuperType) {
when(s) {
is T -> // Do something
}
}
If use: s as T - this cast will show warning (unsafe cast).
How check that s is T type?
If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.
class Generic<T : Any>(val klass: Class<T>) {
companion object {
inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
}
fun checkType(t: Any) {
when {
klass.isAssignableFrom(t.javaClass) -> println("Correct type")
else -> println("Wrong type")
}
}
}
fun main(vararg args: String) {
Generic<String>().checkType("foo")
Generic<String>().checkType(1)
}
Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.
A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.
I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.
It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.
Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.
You could use a function like that:
inline fun <reified T> checkType() = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
however, functions that call it must also be inline, so you might have to write something like
inline fun <reified T> someOtherFunction(data: T) {
checkType<T>
}
however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example
class AmazingTypes {
inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
}
fun myAwesomeMethod(someParameter: Any) {
val amazingClass = AmazingClass()
amazingClass.checkType(someParameter)
}
This is also example.
inline fun <reified T: ApiResponse> parseJson(body: String): T {
// handle OkResponse only
val klass = T::class.java
if (klass.isAssignableFrom(OkResponse::class.java)) {
return T::class.java.newInstance()
}
// handle others
return gson.from(body, T::class.java)
}