Analog for operator :: in Kotlin - java

I can't fully understand how you can convert the code
futuresList.stream().map(CompletableFuture::join).collect(Collectors.toList())
from Java to Kotlin code
I have a list of CompletableFuture and I want to combine for CompletableFuture.allOf
P.S. val futuresList : MutableList<CompletableFuture<String>>

Your original code is almost a working one, you just need to specify the type parameter:
futuresList.stream().map(CompletableFuture<String>::join).collect(Collectors.toList())
Honestly, I'm not sure why this is required and why Kotlin does not use type inference in such a case. Alternatively, we can do it like this:
futuresList.stream().map { it.join() }.collect(Collectors.toList())
I believe this approach is more common in Kotlin.
Also, I'm not sure why do you use stream here. It seems the same as mapping the list directly:
futuresList.map { it.join() }

Here's the kotlint code, which shows how to achieve what you want ...
fun main() {
var newList = listOf(1, 2).map(Adder.Companion::addOne)
newList.forEach { println(it) }
// Will print 2, 3
}
class Adder {
companion object {
fun addOne(x: Int): Int {
return x + 1
}
}
}
Here's a working example.

Related

Is there any special method that adding document of firebase into arrayList(Kotlin)? [duplicate]

I'm building an app for a friend and I use Firestore. What I want is to display a list of favorite places but for some reason, the list is always empty.
I cannot get the data from Firestore. This is my code:
fun getListOfPlaces() : List<String> {
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
return list;
}
If I try to print, let's say the size of the list in onCreate function, the size is always 0.
Log.d("TAG", getListOfPlaces().size().toString()); // Is 0 !!!
I can confirm Firebase is successfully installed.
What am I missing?
This is a classic issue with asynchronous web APIs. You cannot return something now, that hasn't been loaded yet. With other words, you cannot simply return the places list as a result of a method because it will always be empty due the asynchronous behavior of the onComplete function. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available.
But not only Cloud Firestore loads data asynchronously, almost all of modern other web APIs do, since it may take some time to get the data. But let's take an quick example, by placing a few log statements in the code, to see more clearly what I'm talking about.
fun getListOfPlaces() : List<String> {
Log.d("TAG", "Before attaching the listener!");
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d("TAG", "Inside onComplete function!");
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
Log.d("TAG", "After attaching the listener!");
return list;
}
If we run this code will, the output in your logcat will be:
Before attaching the listener!
After attaching the listener!
Inside onComplete function!
This is probably not what you expected, but it explains precisely why your places list is empty when returning it.
The initial response for most developers is to try and "fix" this asynchronous behavior, which I personally recommend against it. Here is an excelent article written by Doug Stevenson that I'll highly recommend you to read.
A quick solve for this problem would be to use the places list only inside the onComplete function:
fun readData() {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
//Do what you need to do with your list
}
}
}
If you want to use the list outside, there is another approach. You need to create your own callback to wait for Firestore to return you the data. To achieve this, first you need to create an interface like this:
interface MyCallback {
fun onCallback(value: List<String>)
}
Then you need to create a function that is actually getting the data from the database. This method should look like this:
fun readData(myCallback : MyCallback) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback.onCallback(list)
}
}
}
See, we don't have any return type anymore. In the end just simply call readData() function in your onCreate function and pass an instance of the MyCallback interface as an argument like this:
readData(object: MyCallback {
override fun onCallback(value: List<String>) {
Log.d("TAG", list.size.toString())
}
})
If you are using Kotlin, please check the other answer.
Nowadays, Kotlin provides a simpler way to achieve the same result as in the case of using a callback. This answer is going to explain how to use Kotlin Coroutines. In order to make it work, we need to add the following dependency in our build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.2.1"
This library that we use is called Module kotlinx-coroutines-play-services and is used for the exact same purpose. As we already know, there is no way we can return a list of objects as a result of a method because get() returns immediately, while the callback from the Task it returns will be called sometime later. That's the reason why we should wait until the data is available.
When calling "get()" on the Task object that is returned, we can attach a listener so we can get the result of our query. What we need to do now is to convert this into something that is working with Kotlin Coroutines. For that, we need to create a suspend function that looks like this:
private suspend fun getListOfPlaces(): List<DocumentSnapshot> {
val snapshot = placesRef.get().await()
return snapshot.documents
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. Now we can simply call it from another suspend method like in the following lines of code:
private suspend fun getDataFromFirestore() {
try {
val listOfPlaces = getListOfPlaces()
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}
The reason for having a empty list got perfectly answered by Alex Mamo above.
I just like to present the same thing without needing to add an extra interface.
In Kotlin you could just implement it like so:
fun readData(myCallback: (List<String>) -> Unit) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback(list)
}
}
}
and then use it like so:
readData() {
Log.d("TAG", it.size.toString())
})

Java/Kotlin callback syntax - do I really need 2 callback definitions after conversion to Kotlin?

I have a problem that has been challenging me for a few days with no resolution (more directly, no resolution I feel is correct). The issue is around callbacks, Java implementation vs Kotlin implementation
I have this Java method:
public void setOnSelectionChange(MapControlUpdate mapMenuControl) {
this.mapControlUpdate = mapMenuControl;
}
private MapControlUpdate mapControlUpdate;
public interface MapControlUpdate {
void onSelectionChange(MAP_TYPE mapType);
}
Using the above implementation I have what I want (below) in both Java and Kotlin.
Java (before):
widgetMapType.setOnSelectionChange(mapType -> {
Toast.makeText(getContext(), "clicked: " + mapType, Toast.LENGTH_LONG).show();
});
Kotlin (before):
widgetMapType.setOnSelectionChange {
Toast.makeText(context, "clicked: $it", Toast.LENGTH_LONG).show()
}
The new Kotlin code, after conversion is:
fun setOnSelectionChange(mapMenuControl: MapControlUpdate?) {
mapControlUpdate = mapMenuControl
}
private var mapControlUpdate: MapControlUpdate? = null
After the conversion to Kotlin the Java usage remains unchanged but I need to change the Kotlin code as follows or I get a syntax error:
Kotlin (after):
widgetMapType.setMapMenuControl(object: WidgetMapType.MapControlUpdate {
override fun onSelectionChange(mapType: WidgetMapType.MAP_TYPE?) {
Toast.makeText(context, "clicked: $mapType", Toast.LENGTH_LONG).show()
}
})
In order to get back to where I'd like to be I found that the only solution appear to be to implement 2 callbacks; 1 to allow Java to work with the original syntax and another to allow Kotlin syntax to remain the same.
This is the code I'm using (it works):
var onSelectionChange: MapControlUpdate? = null
private var onSelectionChangeListener: ((MapDisplayTypes?) -> Unit)? = null
fun setOnSelectionChange(listener: (MapDisplayTypes?) -> Unit){
onSelectionChangeListener = listener
}
and I fire both callbacks as appropriate
onSelectionChange?.onSelectionChange(it) // Java
onSelectionChangeListener?.invoke(it) // Kotlin
I really cannot believe that there isn't a more correct method but my searches (here and on the web) have returns tons of examples for Kotlin and Java but they all align with my above examples based on the code (also shown above). I suspect there maybe an annotation or something that I'm missing so finding no other solution I'm turning to the community here.
Thank you ahead of time!!
I suspect that you will need to keep just your interface MapControlUpdate definition in Java.
Instead of two callbacks you would keep just one callback and convert as appropriate:
var onSelectionChange: MapControlUpdate? = null
fun setOnSelectionChange(listener: (WidgetMapType.MAP_TYPE?) -> Unit){
onSelectionChangeListener = object : MapControlUpdate {
override fun onSelectionChange(mapType: WidgetMapType.MAP_TYPE?) {
listener(mapType)
}
}
}
Or write a helper function if MapControlUpdate is used more than once.
But the real solution is, as karmakaze says: keep the interface in Java until Kotlin 1.4 and then declare it as fun interface.

Kotlin, Reactive programming : How to consume the value of one function output to another one

I am very new to Project reactor library and reactive programming with Kotlin, and trying to implement functions like flatmap, flatMapIterable, subscribe etc.
Now issue is I am trying to use the o/p of one function into another one using flatMapIterable, and after using I am trying to subscribe this, by passing the output of fist function and second one to another function of new class.
Now when I try to use the o/p of function 1, I am unable to see the value, I only see Mono<> or Flux<>.
Below is code snippet for more explanation
var result = employerService.getEmployee("Active") // return value is Mono<GetEmployeeStatusListResult>
result.flatMapIterable(GetEmployeeStatusListResult::emps)
.flatMap {
employerService.getUsersById(it.userId) // it is of type GetEmployeeStatusListResult.emps and value returned from employerService.getUsersById(it.userId) is of type GetUserResult class created
}.subscribe {
aService.createContact(result, it)
}
Now at line 4 I am getting expected userId out of it.userId, but when I inspect result at line 6, then I do not get the expected list of values, it just provides me MonomapFuesable, with mapper and source.
Can anyone please help me to understand what should I do, as my whole agenda is to pass the calculated value from line 1 and line 4 to line 6 function.
Please ask more question, if I haven't provided the required information, I am very new to this.
Thanks in advance !!
[UPDATE] : I have resolved the issue with the following way :
```
employerService.getEmployee("Active") // return value is Mono<GetEmployeeStatusListResult>
.flatMapIterable(GetEmployeeStatusListResult::emps)
.flatMap {
employerService.getUsersById(it.userId).map{x->Pair(it,x)} // it is of type GetEmployeeStatusListResult.emps and value returned from employerService.getUsersById(it.userId) is of type GetUserResult class created
}.subscribe {
aService.createContact(it.first, it.second)
}
```
It's a bit hard to know for sure from the information supplied above, but I think it looks like the call to employerService.getUsersById isn't returning a Publisher. From your comments I'm guessing it's returning an actual value, GetUserResult, rather than a Mono. Below is a mocked up set of classes which show the desired result, I believe. Maybe compare the below to what you've got and see if you can spot a difference?
data class Employee(val userId: String)
data class GetEmployeeStatusListResult(val emps: List<Employee>)
data class GetUserResult(val employee: Employee)
class EmployerService {
fun getEmployee(status: String) = Mono.just(GetEmployeeStatusListResult(listOf(Employee("a"))))
fun getUsersById(userId: String) = Mono.just(GetUserResult(Employee("a")))
}
fun test() {
val employerService = EmployerService()
employerService
.getEmployee("Active")
.flatMapIterable(GetEmployeeStatusListResult::emps)
.flatMap {
employerService.getUsersById(it.userId)
}.subscribe {
// Here "it" is a GetUserResult object
}
}
If, in the subscribe, you need both the initial value retrieved from the call to getEmployee and also the result of the call to getUsersById then you could wrap those two values in a Pair as shown below:
employerService
.getEmployee("Active")
.flatMapIterable(GetEmployeeStatusListResult::emps)
.flatMap { emp ->
employerService.getUsersById(emp.userId).map { emp to it }
}.subscribe {
// Here "it" is a Pair<Employee, GetUserResult>
}
employerService.getEmployee("Active") // return value is Mono<GetEmployeeStatusListResult>
.flatMapIterable(GetEmployeeStatusListResult::emps)
.flatMap {
employerService.getUsersById(it.userId).map{x->Pair(it,x)} // it is of type GetEmployeeStatusListResult.emps and value returned from employerService.getUsersById(it.userId) is of type GetUserResult class created
}.subscribe {
aService.createContact(it.first, it.second)
}
Adding pair function to fetch both the values and use it in subscribe block !!
Thanks everyone !!

Callback with parameters with Kotlin

I just started Kotlin so please be nice :)
I have a class that is responsible for fetching some data and notify the main activity that its need to update its UI.
So i have made a function in my DataProvider.kt :
fun getPeople(fromNetwork: Boolean, results: ((persons: Array<Person>, error: MyError?) -> Unit)) {
// do some stuff stuff
val map = hashMapOf(
"John" to "Doe",
"Jane" to "Smith"
)
var p = Person(map)
val persons: Array <Person> = arrayOf (p)
results(persons, null)
}
So i want to call this from my activity but i can't find the right syntax ! :
DataProvider.getPeople(
true,
results =
)
I have try many things but i just want to get my array of persons and my optional error so i can update the UI.
The goal is to perform async code in my data provider so my activity can wait for it.
Any ideas ? Thank you very much for any help.
This really depends on how you define the callback method. If you use a standalone function, use the :: operator. First (of course), I should explain the syntax:
(//these parenthesis are technically not necessary
(persons: Array<Person>, error: MyError?)//defines input arguments: an Array of Person and a nullable MyError
-> Unit//defines the return type: Unit is the equivalent of void in Java (meaning no return type)
)
So the method is defined as:
fun callback(persons: Array<CustomObject>, error: Exception?){
//Do whatever
}
And you call it like:
DataProvider.getPeople(
true,
results = this::callback
)
However, if you use anonymous callback functions, it's slightly different. This uses lambda as well:
getPeople(true, results={/*bracket defines a function. `persons, error` are the input arguments*/persons, error -> {
//do whatever
}})
Yes Kotlin has a great way of using callback functions which I will show you an example of how I use them below:
fun addMessageToDatabase(message: String, fromId: String, toId: String,
addedMessageSuccessHandler: () -> Unit,
addedMessageFailureHandler: () -> Unit) {
val latestMessageRef = mDatabase.getReference("/latest-messages/$fromId/$toId")
latestMessageRef.setValue(message).addOnSuccessListener {
latestMessageUpdateSuccessHandler.invoke()
}.addOnFailureListener {
latestMessageUpdateFailureHandler.invoke()
}
}
And finally you can utilise the new callbacks with the following code
databaseManager.updateLatestMessageForUsers(message, fromId, toId,
latestMessageUpdateSuccessHandler = {
// your success action
},
latestMessageUpdateFailureHandler = {
// your failure action
})
So basically when I successfully add a new row to my database I'm invoking a success or a failure response to the caller of the service. Hopefully this will help out someone.

how to port java inner function in Scala?

How can I port a java inner function from here
which fully is contained in to Scala?
JavaPairRDD<Envelope, HashSet<Point>> castedResult = joinListResultAfterAggregation.mapValues(new Function<HashSet<Geometry>,HashSet<Point>>()
{
#Override
public HashSet<Point> call(HashSet<Geometry> spatialObjects) throws Exception {
HashSet<Point> castedSpatialObjects = new HashSet<Point>();
Iterator spatialObjectIterator = spatialObjects.iterator();
while(spatialObjectIterator.hasNext())
{
castedSpatialObjects.add((Point)spatialObjectIterator.next());
}
return castedSpatialObjects;
}
});
return castedResult;
My approach as outlined below would not compile due to some NotinferredU
val castedResult = joinListResultAfterAggregation.mapValues(new Function[java.util.HashSet[Geometry], java.util.HashSet[Point]]() {
def call(spatialObjects: java.util.HashSet[Geometry]): java.util.HashSet[Point] = {
val castedSpatialObjects = new java.util.HashSet[Point]
val spatialObjectIterator = spatialObjects.iterator
while (spatialObjectIterator.hasNext) castedSpatialObjects.add(spatialObjectIterator.next.asInstanceOf[Point])
castedSpatialObjects
}
})
When asking a question about compilation errors please provide the exact error, especially when your code doesn't stand on its own.
The inner function itself is fine; my guess would be that due to changes above joinListResultAfterAggregation isn't a JavaPairRDD anymore, but a normal RDD[(Envelope, Something)] (where Something could be java.util.HashSet, scala.collection.Set or some subtype), so its mapValues takes a Scala function, not a org.apache.spark.api.java.function.Function. Scala functions are written as lambdas: spatialObjects: Something => ... (the body will depend on what Something actually is, and the argument type can be omitted in some circumstances).
How about this ?
val castedResult = joinListResultAfterAggregation.mapValues(spatialObjects => {
spatialObjects.map(obj => (Point) obj)
})

Categories

Resources