Suppose a Library's method takes a callback handler Handler, but for handling the result, the same logic is used over and over again. So, I created a class SimpleHandler that has the logic. The users that use that class are suppose to override its methods onSuccess onFail, if necessary. But if the handling is not necessary for the specific case, the user may not override them, and the SimpleHandler's default handler will be used.
The problem is that when I auto-complete the methods, the IDE automatically inserts super calls, like super.onFail(). For this code, as you see below, if it is overridden, the super method should not be called. Is there any way to mark that the super method should not be called when overridden, so the IDE will stop inserting it?
interface Handler
{
fun onFinished(result:Int);
}
abstract class SimpleHandler:Handler
{
override fun onFinished(result: Int)
{
if(result==0)
{
onSuccess();
}
else
{
onFail();
}
}
open fun onSuccess()
{
println("The developer is so lazy that he did not do anything.");
}
open fun onFail()
{
println("The developer is so lazy that he did not do anything.");
}
}
fun doSomething(handler:Handler)
{
handler.onFinished(10);
}
fun main(args: Array<String>)
{
doSomething(object: SimpleHandler(){
override fun onSuccess() {
println("I won! I won!");
}
override fun onFail() {
//super.onFail();
println("Me fail English?");
}
})
}
In IDEA/Android Studio, this is governed by Editor > File and Code Templates under Settings.
On my installation, I see Overridden Method Body and Overridden Scala Method Body but no special template for Kotlin. So I would expect Kotlin to be using the Java template.
You can change this template not to call super for a given method name, but it looks like you don't have access to the supertype's name. See the linked documentation for how templates can be shared.
AFAIK, there is no problem in writing something like this:
fun doSomething(handler:Handler, result: Int) {
handler.onFinished(result)
}
fun main() {
doSomething(object: SimpleHandler() {
override fun onFail() {
println("My custom message")
}
}, 10)
}
fun main2() {
doSomething(object : SimpleHandler() {
override fun onSuccess() {
println("My other custom message")
}
}, 0)
}
The developer only needs to override, if needed, the callback he's interested in.
In this case, in main() fails, it'll print "my custom message". If it succeeds, the default onSuccess() will be called.
The reverse is also true for main2(): If it succeeds, it'll print "My other custom message", but it it fails, it'll simply call the default onSuccess.
Tested on AS 3.1.1 with org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.31
You would need to define a separate lint detector for that.
Lint highlights overrides which should call super, but don't, by use of #CallSuper annotation, which is what this Lint detector enables:
https://cs.android.com/android/platform/superproject/+/master:tools/base/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
Granted that might be something new to learn first, but writing custom Lint rules is not that hard. These are some good starting points:
Android Lint Tool Guide https://developer.android.com/studio/write/lint.html
2017 Kotlin Conference Lint Talk
https://www.youtube.com/watch?v=p8yX5-lPS6o
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
Do we have a way to do some action with a lazy initializing object right after creation?
For example, smth like this:
val lazyInitObject by createLazyObject()
.apply {
// do some action with just created lazyInitObject
}
Not sure what your createLazyObject() is here, but with the standard lazy delegate you have full control over the initialization lambda:
val lazyInitObject by lazy {
createTheValue().also {
// do something with it
}
}
So if you want to allow this kind of thing you might want to design your custom delegate differently so it accepts a lambda.
Note that if what you want to do is only about changing the state of the lazily created object and not the enclosing class, you may want to simply use an init { } block in the object's class itself.
Using the build in object I can describe some of how Kotlin will allow you to do this, and will allow you to define this more specifically for your use case.
This can absolutely be accomplished with built in tools, if you need a more specific usage, the .also {} standard function will help you tack on functionality after any call
object MyObject {
fun myFunctions() {
println("I have done my work - MyObject")
}
init {
// Calls on first reference...
println("Hello world! - I am initialized - MyObject")
}
}
println("My app has started")
MyObject.myFunctions()
/*
This prints:
My app has started
Hello world! - I am initialized - MyObject
I have done my work - MyObject
*/
interface ILazy {
fun myFunction()
}
val myLazy: ILazy by lazy {
val myObject = object : ILazy {
override fun myFunction() {
println("myLazy has done its work")
}
init {
println("Object declarative is run instantly - MyLazy")
}
}
println("Post object declaration - Lazy")
myObject
}
myLazy.myFunction()
/*
This prints:
Object declarative is run instantly - MyLazy
Post object declaration - Lazy
myLazy has done its work
*/
Hello dear reactive programmers, I started to learn project reactor but I still struggle to figure out what operator to use when. I figured out, that if I want to have reusable parts to define a reactor flow, I can use the transform operator. What I would like to achieve is to use a certain implementation of such a flow function based on the current observables context. For a Mono flow, I came up with this, but I am very unsure, if it is a good solution:
So here is a part of the flow
class CloudeventOverDelegatorRoute(
val fromHttpToDelegatorRoute: FromHttpToDelegatorRoute,
val delegatorProvider: DelegatorProvider,
val fromDelegatorToHttpRoute: FromDelegatorToHttpRoute
): MessageRoute<HttpBaseMessage, HttpResponseMessage> {
override fun isHandlerFor(context: RouteContext): Boolean {
return fromHttpToDelegatorRoute.isHandlerFor(context)
&& fromDelegatorToHttpRoute.isHandlerFor(context)
}
override fun buildPipeline(input: Mono<RoutableMessage<HttpBaseMessage>>): Mono<RoutableMessage<HttpResponseMessage>> {
var dynamicallyDeterminedDelegator: Delegator? = null
return input.transform {
fromHttpToDelegatorRoute.buildPipeline(input)
}.handle<RoutableMessage<InternalMessage>> { t, u ->
dynamicallyDeterminedDelegator = delegatorProvider.provideDelegatorFor(t.routeContext)
u.next(t)
u.complete()
}.transform {
dynamicallyDeterminedDelegator!!.sendDelegated(it)
}.transform { fromDelegatorToHttpRoute.buildPipeline(it) }
}
}
Here is the dynamic selection logic
interface DelegatorProvider {
fun provideDelegatorFor(context: RouteContext): Delegator
}
class FirstMatchDelegatorProvider(
private val delegators: List<Delegator>
): DelegatorProvider {
override fun provideDelegatorFor(context: RouteContext): Delegator {
return delegators.firstOrNull {
it.isHandlerFor(context)
}?: throw IllegalStateException("No Delegator route available for context: $context")
}
}
And this is the delegator providing an essential sub-part of the whole flow
interface Delegator {
fun isHandlerFor(context: RouteContext): Boolean
fun sendDelegated(input: Mono<RoutableMessage<InternalMessage>>): Mono<RoutableMessage<InternalStatusMessage>>
}
What do you think? How would you solve it?
this approach is problematic because it relies on shared state (the dynamicallyDeterminedDelegator variable). If multiple subscribers subscribe to the returned Mono, they could overwrite each other delegator. Maybe that (multiple subscriptions) can't happen in your application, but this is a very bad habit to get into in any case.
looks like you can derive a delegator out of a RoutableMessage<InternalMessage> , and that you don't really need to retain that delegator.
the easiest way to resolve and apply the delegator to the routableMessage in one go is simply to use flatMap. see the (pseudo) java code below:
.flatMap(routableMessage -> {
val delegator = delegatorProvider.provideDelegatorFor(routableMessage.routeContext);
return delegator.sendDelegated(routableMessage);
})
In kotlin native there is memScoped function that automatically free allocated memory when control is going out of scope.
Is there something like destructors for local objects?
Current Kotlin/Native does not provide mechanism for calling a method when certain object is no longer needed in memory (finalizer in Java speech) but inline lambdas easily allow to implement mechanisms, similar to RAII in C++. For example, if you want to be sure, that some resource is always released after leaving certain scope, you may do:
class Resource {
fun take() = println("took")
fun free() = println("freed")
}
inline fun withResource(resource: Resource, body: () -> Unit) =
try {
resource.take()
body()
} finally {
resource.free()
}
fun main(args: Array<String>) {
withResource(Resource()) {
println("body")
}
}
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.