Unit can't be called in this context by implicit receiver - kotlin

I am following this Kotlin example (https://www.jetbrains.com/help/teamcity/kotlin-dsl.html#Editing+Kotlin+DSL) and trying to write a kotlin script for my CI.
This is my code snippet
steps {
script {
name = "Style check"
id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
I get an Error for the id() call which says
What does the error message mean?
How can I use id() call as given in the example?

This error happens because the method id(String) is defined in an outer scope, and to prevent you from accidentally using the wrong method, Kotlin gives you a compiler error.
In your case you should make sure that there's no other id that you want to use. Perhaps you wanted to use the property named id instead of the method?
Note that neither of the options below may have the same effect as you want. There might be a reason why the API is written like this to not allow you to call methods from outer receivers from inside inner scopes.
In order to use an explicit receiver, you can use this#methodName to call it. In this case, this#steps.
steps {
script {
name = "Style check"
this#steps.id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
}
To understand exactly what's going on, and another way of using an explicit receiver, you could also do something like the below, where you save the this scope in a variable and then call it inside the script scope.
steps {
val stepsThis = this
script {
name = "Style check"
stepsThis.id("StyleCheck")
enabled = false
scriptContent = """
#!/bin/bash
make docker run="make ci lint"
""".trimIndent()
}
}

I also got the same error when working with jetpack compose when I was writing the code below.
In my case the context was not clear.
It was giving this error.
'fun item(key: Any? = ..., content: LazyItemScope.() -> Unit): Unit' can't be called in this context by implicit receiver. Use the explicit one if necessary
It says Unit can't be called in this context. Therefore I changed the context and everything got right.
Error code:
LazyColumn {
items(items = items) { word ->
if (word != null) {
WordColumnItem(word = word) {
onSelected(word)
}
}
//Notice the context
if(items.itemCount == 0) {
item(
content = { EmptyContent("No words") }
)
}
}
}
Correct code:
LazyColumn {
items(items = items) { word ->
if (word != null) {
WordColumnItem(word = word) {
onSelected(word)
}
}
}
//context changed.
if(items.itemCount == 0) {
item(
content = { EmptyContent("No words") }
)
}
}
Although I don't know how to use the explicit receiver.(if necessary)
Solution 1) I think error is clear.
2) You can use explicit receiver.

Related

Why is the value not entering the list?

At 'urichecking2' log, I can see there is value. But in 'uriChecking' the uriList is null.
why the uriList.add not work??
private fun getPhotoList() {
val fileName = intent.getStringExtra("fileName")
Log.d("fileNameChecking", "$fileName")
val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName!!)
var tmpUrl:Uri = Uri.parse(fileName)
Log.d("firstTmpUri","$tmpUrl")
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
tmpUrl = task.result
Log.d("secondTmpUri","$tmpUrl")
Log.d("urichecking2","$task.result")
uriList.add(task.result)
} else {
}
}.addOnFailureListener {
// Uh-oh, an error occurred!
}
}
}
Log.d("thirdTmpUri","$tmpUrl")
Log.d("urichecking", "$uriList")
}
If I do this, the log is output in the order of first, third, and second, and the desired value is in second, but when third comes out, it returns to the value of first.
The listAll method (like most cloud APIs these days, including downloadUrl which you also use) is asynchronous, since it needs to make a call to the server - which may take time. This means the code executes in a different order than you may expect, which is easiest to see if you add some logging:
Log.d("Firebase","Before starting listAll")
listRef.listAll()
.addOnSuccessListener { listResult ->
Log.d("Firebase","Got listResult")
}
Log.d("Firebase","After starting listAll")
When you run this code it outputs:
Before starting listAll
After starting listAll
Got listResult
This is probably not the order you expected, but it perfectly explains why you can't see the list result. By the time your Log.d("urichecking", "$uriList") runs, none of the uriList.add(task.result) has been called yet.
The solution for this is always the same: any code that needs the list result, has to be inside the addOnCompleteListener callback, be called from there, or be otherwise synchronized.
So in its simplest way:
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
uriList.add(task.result)
Log.d("urichecking", "$uriList")
}
}
}
}
This is an incredibly common mistake to make if you're new to programming with asynchronous APIs, so I recommend checking out
Asynchronous programming techniques in the Kotlin language guide
How to get URL from Firebase Storage getDownloadURL
Can someone help me with logic of the firebase on success listener
Why does my function that calls an API or launches a coroutine return an empty or null value?

Kotlin ConflatedBroadcastChannel.offer() doesn't work?

I am sending a value via MyRepository.myConflatedChannel.offer(myvalue).
I then expect to receive it in collect { } or onEach { } blocks in my ViewModel. However, neither function is invoked. It is as if nothing is passed down the ConflatedBroadcastChannel.
Has anybody seen a similar problem?
Make sure you properly work with receiving values.
If you use the ConflatedBroadcastChannel, you can use either OpenSubscription to get a ReceiveChannel or you can represent it as flow (with asFlow).
Note that consume and consumeEach are terminal, they perform an action and then cancel the channel after the execution of the block. See this.
First case:
val receivingChannel = MyRepository.myConflatedChannel.openSubscription()
// then you can consume values using for example a for loop, e.g.:
launch {
for (value in receivingChannel) {
// do something
}
}
Second case:
val receivingFlow = MyRepository.myConflatedChannel.asFlow()
launch {
receivingFlow.collect {
// do something
}
}

Kotlin map a string to another type?

In swift, I can do
"Some String".map { SomeObject($0) }
In kotlin it seems like the string is treated as a char array so the result is the map of each character. Is it possible to get similar behavior like the swift code I posted?
"Some String".map { SomeObject(it) }
You can accomplish something like that with let:
"Some String".let { SomeObject(it) }
If you have an appropriate constructor in place (e.g. constructor(s : String) : this(...)) you can also call it as follows:
"Some String".let(::SomeObject)
run and with work also, but are usually taken if you want to rather call a method of the receiver on it. Using run/with for this would look as follows:
"Some String".run { SomeObject(this) }
with ("Some String") { SomeObject(this) }
// but run / with is rather useful for things like the following (where the shown function calls are functions of SomeObject):
val x = someObject.run {
doSomethingBefore()
returningSomethingElse()
}
Besides using let, run or with, you can also write an extension method:
fun String.toSomeObject() = SomeObject(this)
Then use it like follows:
"SomeObject".toSomeObject()

Return from `buildSequence` in Kotlin

I'm using the buildSequence function in Kotlin. How do I end the iteration in the middle of the function? I'm looking for something similar to C#'s yield break statement.
My code looks something like the following. I'm stuck at the TODO.
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
if (someCondition) {
// TODO: Bail out early with an empty sequence
// return doesn't seem to work....
}
list.forEach {
yield(someProcessing(it))
}
}
EDIT
Apparently, I misdiagnosed the source. The issue is not returning from the buildSequence function. The following works for me:
fun foo(list:List<Number>): Sequence<Number> = buildSequence {
return#buildSequence
list.forEach {
yield(someProcessing(it))
}
}
EDIT 2
The issue is that I put the return in a local helper function that validates data at multiple points in the buildSequence (Hence the helper function). Apparently I'm not able to return from buildSequence within the helper function. The error message was not terribly helpful...
Just use return#buildSequence, which is a labeled return from lambda, while an unlabeled return would mean 'return from the function foo'.
See also: Whats does “return#” mean?
Since Kotlin v 1.3.x preferred sequence syntax changed. (buildSequence is replaced by kotlin.sequences.sequence)
Updated "early return from generator" code snippet (includes try-catch and == null early return examples) for post 1.3.x Kotlin:
// gen# is just a subjective name i gave to the code block.
// could be `anything#` you want
// Use of named returns prevents "'return' is not allowed here" errors.
private fun getItems() = sequence<Item> gen# {
val cursor: Cursor?
try {
cursor = contentResolver.query(uri,*args)
} catch (e: SecurityException) {
Log.w(APP_NAME, "Permission is not granted.")
return#gen
}
if (cursor == null) {
Log.w(APP_NAME, "Query returned nothing.")
return#gen
}
// `.use` auto-closes Closeable. recommend.
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
cursor.use {
// iterate over cursor to step through the yielded records
while (cursor.moveToNext()) {
yield(Item.Factory.fromCursor(cursor))
}
}
}
(Thx for all the prior posts that helped me get on "named return" track.)

Hacking into java.lang.Object: calling custom external class crashes JVM

I'm playing around with editing java.lang.Object for the Java Runtime Environment. I realize that there are probably better ways to do what I want, but that is not what my question is about.
Basically I've added a constructor to java.lang.Object which gets called everytime an object is created. I'm waiting for a certain class to load like so:
public Object() {
if (hookEnabled) {
hookEnabled = false;
objectCount++;
if (objectCount > objectStartCount) {
if (this.getClass() != null) {
String name = this.getClass().getName();
if ((!name.startsWith("java.")) && (!name.startsWith("javax.")) && (!name.startsWith("launcher.")) && (!name.startsWith("sunw.")) && (!name.startsWith("com.sun.")) && (!name.startsWith("sun.")) && (!name.startsWith("org.xml.")) && (!name.startsWith("org.w3c.")) && (!name.startsWith("org.omg.")) && (!name.startsWith("org.ietf."))) {
if (!hasHooked) {
hasHooked = true;
//startup beep
java.awt.Toolkit.getDefaultToolkit().beep();
//load interface
javax.swing.JFrame frame = new javax.swing.JFrame("");
frame.setBounds(0, 0, 400, 400);
frame.setAlwaysOnTop(true);
frame.setVisible(true);
}
}
}
}
hookEnabled = true;
}
}
This works fine. It adds a window to whatever application is being run by the JVM.
However, when making a simple change by moving the JFrame code into a separate class, and calling that call the JVM simply crashes:
public Object() {
if (hookEnabled) {
hookEnabled = false;
objectCount++;
if (objectCount > objectStartCount) {
if (this.getClass() != null) {
String name = this.getClass().getName();
if ((!name.startsWith("java.")) && (!name.startsWith("javax.")) && (!name.startsWith("launcher.")) && (!name.startsWith("sunw.")) && (!name.startsWith("com.sun.")) && (!name.startsWith("sun.")) && (!name.startsWith("org.xml.")) && (!name.startsWith("org.w3c.")) && (!name.startsWith("org.omg.")) && (!name.startsWith("org.ietf."))) {
if (!hasHooked) {
hasHooked = true;
(new tvmh.DFVMH()).setup();
}
}
}
}
hookEnabled = true;
}
}
--
package tvmh;
public class DFVMH {
public void setup() {
//startup beep
java.awt.Toolkit.getDefaultToolkit().beep();
//load interface
javax.swing.JFrame frame = new javax.swing.JFrame("");
frame.setBounds(0, 0, 400, 400);
frame.setAlwaysOnTop(true);
frame.setVisible(true);
}
}
The same happens when I try to create a java.util.Timer object.
Interestingly enough, the above does work if I make DFVMH an inline class (internal class) of java.lang.Object itself.
Could anyone tell me why such behaviour would happen? And is there any way to safely call such custom class?
Tinkering with the innards of the JVM like this is very risky. There are all sorts of hidden dependencies at the low levels of the JVM that can break. JVM bootstrap is a very delicate process.
For instance, the most likely reason you are seeing a crash rather a StackOverflowError is that your change has broken all object construction ... including construction of the error object.
And I suspect that your guard code is ineffective because this.getClass().getName() may be causing a String object to be created. So the fatal recursion happens before you get to your guard.
(Incidentally, your hasHooked flag introduces a race condition.)
My advice is "Don't do it!".
What do you mean by 'it crashes'?.
Isn't it StackOverflowException? Your new tvmh.DFVMH() is actually a constructor all too. So it runs through your 'overriden' Object constructor.
If you already play like this, how about adding the tvmh.DFVMH to stop list of packages/classes?
Just a quick thought: new tvmh.DFVHM() becomes a new object, which also derives from java.lang.Object, meaning your custom constructor code will be run again before the first one has finished. I'm guessing "hasHooked" should guard against that, but how is that variable defined? If that guard doesn't work, this sequence it will recurse infinitely.
If you make DFVMH an inline class it's name will probably start with "java.lang[...]" (it's in java.lang.Object, after all) and will thus not get through the long if statement with all the name.startsWith.