In my code I have a struct like this
post { req ->
with(req.objectBody<Person>()) {
logger.info { "Attempt to save person $this" }
with(require<SessionFactory>().openSession()) {
save(this#with)
}
}
}
But IDE warns me that there is more than one label with such a name. In this case
save(this#with)
I want to refer to with(req.objectBody<Person>) instance. How to achieve that?
Technically, you can mark lambdas with custom labels and then use labeled this with those labels. such as:
with(foo()) mylabel#{
with(bar()) {
baz(this#mylabel)
}
}
However, to improve readability, instead of with, you can use the let scoping function and provide a name for the parameter:
foo().let { fooResult ->
bar().let { barResult ->
baz(fooResult)
}
}
Related
There are several switches in the app's layout, and when these switches are pressed, the value of sharedPreference is changed to determine whether a specific function is performed. For example, if the funcOnOff switch is off, the voice notification function is off, and when fromOnOff is off, caller information cannot be checked when a notification is received.
I am using several source codes that work almost similarly as below. Is it possible to pass multiple android.widgets as parameters to a function so that these actions can be acted upon as a single function?
var funcOnOff: Switch = findViewById(R.id.func_on_off)
var fromOnOff: Switch = findViewById(R.id.from_on_off)
var timeOnOff: Switch = findViewById(R.id.time_on_off)
var contentOnOff: Switch = findViewById(R.id.content_on_off)
funcOnOff.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
editor.putString("func", "ON")
} else {
editor.putString("func", "OFF")
}
editor.commit()
}
fromOnOff.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
editor.putString("from", "ON")
} else {
editor.putString("from", "OFF")
}
editor.commit()
}
timeOnOff.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
editor.putString("time", "ON")
} else {
editor.putString("time", "OFF")
}
editor.commit()
}
If I understand correctly, you can make a factory method for the OnCheckedChangeListeners.
fun onCheckedChangedListenerForPreferenceKey(key: String): CompoundButton.OnCheckedChangeListener = { _, isChecked ->
if (isChecked) {
editor.putString(key, "ON") // wouldn't using putBoolean be better?
} else {
editor.putString(key, "OFF")
}
editor.commit()
}
Then you can do:
funcOnOff.setOnCheckedChangeListener(onCheckedChangedListenerForPreferenceKey("func"))
fromOnOff.setOnCheckedChangeListener(onCheckedChangedListenerForPreferenceKey("from"))
timeOnOff.setOnCheckedChangeListener(onCheckedChangedListenerForPreferenceKey("time"))
Or you can make a list of pairs of switches to preference keys, and iterate through them:
listOf(
funcOnOff to "func"
fromOnOff to "from"
timeOnOff to "time"
).forEach { (switch, key) ->
switch.setOnCheckedChangeListener(onCheckedChangedListenerForPreferenceKey(key))
}
As a general alternative to Sweeper's answer - most widget listeners (and a lot of other UI callbacks in Android) pass in the object that generated the event as a parameter. So you can create a function that checks that parameter, and acts accordingly:
// If the signature here matches the listener function, you can pass a reference
// to this function directly
fun handleSwitch(switch: CompoundButton, isChecked: Boolean) {
when(switch) {
funcOnOff -> "func"
fromOnOff -> "from"
timeOnOff -> "time"
else -> null
}?.let { key ->
editor.putString(key, if (isChecked) "ON" else "OFF").commit()
}
}
Then you can just apply that to all your switches:
val switches = listOf(funcOnOff, fromOnOff, timeOnOff)
switches.forEach { it.setonOnCheckedChangedListener(::handleSwitch) }
// or if you don't want to use a function reference
switches.forEach {
it.setOnCheckedChangedListener { switch, enabled -> handleSwitch(switch, enabled) }
}
// or since you're iterating over the switches anyway, you could make the current one
// part of the callback lambda and ignore the parameters
switches.forEach { switch ->
it.setOnCheckedChangedListener { _, _ -> handleSwitch(switch, switch.isChecked) }
}
You get the idea! Personally I feel like passing a function reference is neater, instead of creating a separate lambda for each switch, but honestly it depends - no need to overcomplicate things if a neat one-liner setup will get the job done!
Also personally I'd probably use a Map instead of the when, like in Sweeper's answer - that way you can define the switches in use and their keys in one place, and then you can assign your listeners by iterating over the map's keys. No repeating yourself by listing the keys in one place, and then again in the when, y'know? Easier to maintain too, you might forget to update one of those places - but this is just a general example!
Recently, I face this warning in my IntelliJ Idea. But I don't have any solution for that...
Also, I don't want to use the #Suppress("NAME_SHADOWING").
I'll be grateful if you guide me.
This is my code:
fun add(
#Parameter id: Long?
): someClass {
myTable?.content?.firstOrNull { it.add }?.id?.let { id ->
db.products.readById(id)?.let { db.products.delete(it) }
}
return remove(id)
}
Name shadowing means that you are using variables with the same name in different scopes, making it more likely that you by accident refer to the wrong one. The solution is to rename them to be different. In your case it's the variable id. It is both the parameter of the function and it is also defined after the first let. So you could for example do this to remove the warning:
fun add(
#Parameter id: Long?
): someClass {
myTable?.content?.firstOrNull { it.add }?.id?.let { id2 ->
db.products.readById(id2)?.let { db.products.delete(it) }
}
return remove(id)
}
fun add(
#Parameter id: Long?
): someClass {
myTable?.content?.firstOrNull { it.add }?.id
?.let { id ->
// Here you have two variables named "id" (the other being the function parameter)
db.products.readById(id)?.let { db.products.delete(it) }
}
return remove(id)
}
Simply rename one of the parameters and the warning will go away.
How to return from an anonymous lambda in Kotlin?
Somehow the complier doesn't allow to return inside the lambda body. Since the lambda is anonym an return#... isn't possible here.
class Foo {
var function: (String) -> Unit = { _ -> }
init {
function = { text ->
if (text == "foo"){
// do side effects here
return
//'return' is not allowed here
//This function must return a value of type Foo
}
// do side other side effects
}
}
}
EDIT: update the example so it is clear that this question is about the return statement and not coding practices
Use Label:
class Foo {
var function: (String) -> Unit
init {
function = function# { text ->
if (text == "foo"){
return#function
}
print(text)
}
}
}
While it's possible to do, I'm not a fan of that sort of thing and prefer to restructure the flow when practical. In your example, it would be something like:
function = { text ->
if (text == "foo"){
// do side effects here
} else {
// do side other side effects
}
}
There are usually ways to keep the flow to a single return path, so you don't have to do strange things like have multiple return statements or use labels.
I have a method which takes a list of object (Widget) -- which contains some properties (header) and nested list(component). I want to flatten the list into a single list and have the below code for same:
#SuppressLint("CheckResult")
fun flatten(fatList: Single<List<Widget>>) {
val flatList: MutableList<IUiData> = mutableListOf()
fatList.map {
Observable.fromIterable(it).map { widget ->
if (widget.header.isNotEmpty()) {
flatList.add(ProductHeaderUi(widget.header))
}
widget.componentList.map { component ->
when (component.type) {
TILE_TEXT -> {
flatList.add(HeaderUi(component))
}
TILE_IMAGE -> {
flatList.add(ImageTileUi(component))
}
TILE_FOOTER -> {
flatList.add(FooterUi(component))
}
UNKNOWN -> {
//Do Nothing
}
}
}
}
}
}
I intend to return a Single of List: Single<MutableList<IUiData>> from this method, this purpose can be served right now, but I am looking for a cleaner way
You're using both Rx's Observable map and Kotlin's Iterable map in an unintended way. They are for converting one type to another, not for iterating something.
You've also nested an unnecessary Observable iterable inside the outer-most map function.
You only need to map the output of the Single. Inside the map function, you iterate (not map) the original List to pull out the data you need for the MutableList.
I'm an Rx novice and didn't check this, so sorry about any syntax errors.
fun flatten(fatList: Single<List<Widget>>): Single<MutableList<IUData>> = fatList.map { widgetList ->
val flatList: MutableList<IUiData> = mutableListOf()
for (widget in widgetList) {
if (widget.header.isNotEmpty()) {
flatList.add(ProductHeaderUi(widget.header))
}
for (component in widget.componentList) {
when (component.type) {
TILE_TEXT -> flatList.add(HeaderUi(component))
TILE_IMAGE -> flatList.add(ImageTileUi(component))
TILE_FOOTER -> flatList.add(FooterUi(component))
// Else do nothing
}
}
}
flatList
}
But in keeping with typical Rx chaining syntax, I would make it an extension function, so I'd have to first line like this. Then you can put it right in the middle of an Rx call chain:
fun Single<List<Widget>>.flatten(): Single<MutableList<IUData>> = map { widgetList ->
You can also do this in a more concise, functional, but less efficient way by using Kotlin's flatMap:
fun Single<List<Widget>>.flatten(): Single<MutableList<IUData>> = map {
it.flatMap { widget ->
listOfNotNull(widget.header.takeIf(Header::isNotEmpty)?.let(::ProductHeaderUi))
+
widget.componentList.mapNotNull { component ->
when (component.type) {
TILE_TEXT -> HeaderUi(component)
TILE_IMAGE -> ImageTileUi(component)
TILE_FOOTER -> FooterUi(component)
else -> null
}
}.toMutableList()
}
...where Header is whatever type widget.header uses.
In Kotlin DSL example they use plus signs to implement raw content inserting:
html {
head {
title {+"XML encoding with Kotlin"}
}
// ...
}
Is it possible to define "nameless" functions in receiver to be able to write
html {
head {
title {"XML encoding with Kotlin"}
}
// ...
}
Are there any plans to do so in future versions of Kotlin?
Is there such things in languages, other than Kotlin?
I can think of two solutions to your problem:
Make the lambda with receiver return a String:
fun title(init: Title.() -> String) {
val t = Title().apply {
children.add(TextElement(init()))
}
children.add(t)
}
You can now call the title as suggested in OP. Actually this seems to be overhead in this particular scenario though and I'd recommend the following.
Create another title method that takes a String directly:
class Head : TagWithText("head") {
fun title(init: Title.() -> Unit) = initTag(Title(), init)
fun title(text: String) {
val t = Title().apply {
children.add(TextElement(text))
}
children.add(t)
}
}
Used like this:
head {
title("XML encoding with Kotlin")
}