Converting java to kotlin -- streams - kotlin

I am learning Kotlin, and not know it properly. I have a piece of code in Java:
private static Signature[] createSignatures(String... encodedSignatures) {
return Arrays.stream(encodedSignatures)
.map(encodedSignature -> new Signature(Base64.decode(encodedSignature, Base64.DEFAULT)))
.toArray(Signature[]::new);
}
I want to convert it to Kotlin, and I have reached till here:
private static Signature[] createSignatures(String... encodedSignatures) {
return Arrays.stream(encodedSignatures)
.map(encodedSignature -> new Signature(Base64.decode(encodedSignature, Base64.DEFAULT)))
.toArray(Signature[]::new);
}
But the above doesn't work, especially the line at the end, that is, .toArray(Signature[]::new) is the problem. How do I solve it? What is the correct way to convert streams to array in kotlin?

You can achieve your goal by using following code:
private fun createSignatures(vararg encodedSignatures: String): Array<Signature> {
return encodedSignatures
.map { Signature(Base64.decode(it, Base64.DEFAULT)) }
.toTypedArray()
}

Try this:
private fun createSignatures(vararg encodedSignatures: String): Array<Signature?>? {
return Arrays.stream(encodedSignatures)
.map { encodedSignature -> Signature(Base64.decode(encodedSignature, Base64.DEFAULT)) }
.toArray { _Dummy_.__Array__() }
}

Related

Kotlin - Trying to factorize code with high-order function

I'm quite new to Kotlin and I'd like to see if using high-order functions can help in my case.
My use-case is that I need to call the methods of an IInterface derived class to send events to one or more components. And I'd like to make this generic, and I want to check if a high-order funtion can help. A sample of code will help to understand (well, I hope so!).
private val eventListeners = mutableListOf<IEventInterface>() // List filled somewhere else!
private fun sendConnectionEvent(dummyString: String) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onConnectionEvent(dummyString)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
private fun sendWonderfulEvent(dummyString: String, dummyInt: Int) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onWonderfulEvent(dummyString, dummyInt)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
I added 2 similar methods (I will have many more in the real use case) and I think (I hope!) that something could be done but I can't make high-order function works in this case because:
I want to call the same method on several instances, and not 'just' a basic function
To make things even worse, the methods I need to call don't have the same prototype (that would have been too easy!).
Hope this is clear enough.
Thanks for your help!
VR
Here is how it can be done
fun onEvent(body: (IEventInterface) -> Unit) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
body(it)
} catch (ex: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
Supposing an interface like this:
interface IEventInterface {
fun onConnectionEvent(dummyString: String)
fun onWonderfulEvent(dummyString: String, dummyInt: Int)
}
Define an generic type that implements your defined interface ( <T : IEventInterface>)
Define an mutable list of this type to receive your implementation (MutableList<T>.removeIfThrows)
Expect an extension function for you type that will do your specific validation (and custom parameters if you want)
Using an apply and returning the instance you can run your code like a pipeline
Executing the custom validation when you want
private fun <T : IEventInterface> MutableList<T>.removeIfThrows(validation: T.() -> Unit, customLogMessage: String? = null): MutableList<T> {
return apply {
removeIf {
it.runCatching {
validation()
}.onFailure { error ->
print(customLogMessage ?: "Removing listener - Exception ${error.message}")
}.isFailure
}
}
}
Define your specific implementation passing just the function with custom validation as an parameter
private fun <T : IEventInterface> MutableList<T>.sendConnectionEvent(dummyString: String) = removeIfThrows({
onConnectionEvent(dummyString)
})
private fun <T : IEventInterface> MutableList<T>.sendWonderfulEvent(dummyString: String, dummyInt: Int) = removeIfThrows({
onWonderfulEvent(dummyString, dummyInt)
})
Now you can run your code like an pipeline modifying your original object like this
private fun nowYouCanDoSomethingLikeThis() {
eventListeners
.sendConnectionEvent("some dummy string")
.sendWonderfulEvent("some another dummy string", 123)
}

Reduce/Collect `List<Map<String, Set<String>` to `Map<String, Set<String>>`

After performing a parallelStream() on a List, I end up with a List<Map<String, Set<String>. I want to unify this into a Map<String, Set<String>> (which will only keep uniques across the List of Maps).
I am unfamiliar with the collect and reduce functions, so don't have anything to go ahead with.
Existing code:
private val TYPES = listOf("string", "integer")
private fun getLinesOfEachTypeAcrossMultipleFiles(files: List<File>): Map<String, Set<String>> {
return files
.parallelStream()
.map { file ->
TYPES.associate {
it to getRelevantTypeLinesFromFile(file)
}
}
// Converted into a Stream<String, Set<String>>
// .reduce() / collect() ?
}
private fun getRelevantTypeLinesFromFile(it: File): Set<String> {
// Sample code
return setOf()
}
If you're looking for an equivalent Java code, you can stream all the entries using flatMap and then collect them as a Map with a merge function as :
Map<String, Set<String>> some(List<Map<String, Set<String>>> listOfMap) {
return listOfMap.stream()
.flatMap(a -> a.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(s1, s2) -> {
s1.addAll(s2);
return s1;
}));
}
I figured out and implemented a Kotlin-specific solution of using the fold operator (instead of reduce or collect):
private val TYPES = listOf("string", "integer")
private fun getLinesOfEachTypeAcrossMultipleFiles(files: List<File>): Map<String, Set<String>> {
return files
.map { file ->
TYPES.associate { it to getRelevantTypeLinesFromFile(file) }
}
.fold(mutableMapOf<String, MutableSet<String>>()) { acc, map ->
acc.apply {
map.forEach { key, value ->
acc.getOrPut(key) { mutableSetOf() }.addAll(value)
}
}
}
}
private fun getRelevantTypeLinesFromFile(it: File): Set<String> {
// Sample code
return setOf()
}
A benefit of using fold is that we don't need to change the type of the data from Map to MutableMap and Set to MutableSet.

How can I `return` from inside of a call to `use`?

In Kotlin, this code compiles:
private fun bar(): Boolean = TODO()
fun works(): Int {
while (true) {
if (bar()) {
return 5
}
}
}
(This is a pared down example of my real code to illustrate the issue I'm running into.)
I actually need to use a file during this loop, and close on exit:
fun openFile(): InputStream = TODO()
fun doesnt_work(): Int {
openFile().use { input ->
while (true) {
if (bar()) {
return 5
}
}
}
} // line 42
This doesn't compile. I get the error:
Error:(42, 5) Kotlin: A 'return' expression required in a function with a block body ('{...}')
I've found two ways to work around this, but both are kind of awkward.
One way is to use a variable to hold the result, and break from the loop right when it's set:
fun works_but_awkward(): Int {
openFile().use { input ->
val result: Int
while (true) {
if (bar()) {
result = 5
break
}
}
return result
}
}
This is especially awkward in my real code, as I have a nested loop, and so I need to use a labelled break.
The other way to work around this is to have a named function for the loop:
fun workaround_with_named_function(): Int {
fun loop(input: InputStream): Int {
while (true) {
if (bar()) {
return 5
}
}
}
return openFile().use { loop(it) }
}
This seems a bit better, but I'm still surprised that the use abstraction is so leaky that I can't do an early return from within a loop. Is there a way to use use with an early return in a loop that's less awkward?
Cause Kotlin compiler isn't smart enough to undestand that use with code inside will return something from the function. The reason of such behavior is inability to guarantee compiler that lambda will be called exactly once.
Another way to workaround this is throwing exception in the end of the function:
fun doesnt_work(): Int {
openFile().use { input ->
while (true) {
if (bar()) {
return 5
}
}
}
throw IllegalStateException("Something goes wrong")
}
P.S. I am not sure, but seems it can be compiled without any hacks when contract system will be added to Kotlin. And it is probably going to be in version 1.3
This should work.
fun openFile(): InputStream = TODO()
fun doesnt_work(): Int {
return openFile().use { input ->
while (true) {
if (bar()) {
return#use 5
}
}
-1 // unreachable return value
// just to help Kotlin infer the return type
}
}
Remember, use is a function whose return value is exactly the same with the return value of the lambda. So returning the value (here it's 5) in the lambda and return the return value of use should work.
Also, if I were you, I'll write the function like this:
fun doesnt_work() = openFile().use { input ->
while (true) if (bar()) return#use 5
-1
}

Converting Java Parcel creator code to Kotlin

I'm trying to convert my Parcel Creator code from Java to Kotlin. The auto code converter fails at this and I'm not sure how to resolve it. The code I'm converting is:
public static final Creator<Save> CREATOR = new Creator<Save>() {
#Override
public Save[] newArray(int size) {
return new Save[size];
}
#Override
public Save createFromParcel(Parcel incoming) {
return new Save(incoming);
}
};
I end up with this:
val CREATOR: Parcelable.Creator<Save> = object : Parcelable.Creator<Save> {
override fun newArray(size: Int): Array<Save> {
return arrayOfNulls(size)
}
override fun createFromParcel(incoming: Parcel): Save {
return Save(incoming)
}
}
which gives an error saying 'Type inference failed' for arrayOfNulls(size). I have tried changing it to Save(size) but that doesn't work either. Any Ideas?
arrayOfNulls will return an array of nullable elements, Array<Save?> in this case. You should change your function's return type to match that.
override fun newArray(size: Int): Array<Save?> {
return arrayOfNulls(size)
}
You can try this plugin to do Parcelable, just suggestion, I never tried but come across while googling.

How do I write to a file in Kotlin?

I can't seem to find this question yet, but what is the simplest, most-idiomatic way of opening/creating a file, writing to it, and then closing it? Looking at the kotlin.io reference and the Java documentation I managed to get this:
fun write() {
val writer = PrintWriter("file.txt") // java.io.PrintWriter
for ((member, originalInput) in history) { // history: Map<Member, String>
writer.append("$member, $originalInput\n")
}
writer.close()
}
This works, but I was wondering if there was a "proper" Kotlin way of doing this?
A bit more idiomatic. For PrintWriter, this example:
File("somefile.txt").printWriter().use { out ->
history.forEach {
out.println("${it.key}, ${it.value}")
}
}
The for loop, or forEach depends on your style. No reason to use append(x) since that is basically write(x.toString()) and you already give it a string. And println(x) basically does write(x) after converting a null to "null". And println() does the correct line ending.
If you are using data classes of Kotlin, they can already be output because they have a nice toString() method already.
Also, in this case if you wanted to use BufferedWriter it would produce the same results:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.write("${it.key}, ${it.value}\n")
}
}
Also you can use out.newLine() instead of \n if you want it to be correct for the current operating system in which it is running. And if you were doing that all the time, you would likely create an extension function:
fun BufferedWriter.writeLn(line: String) {
this.write(line)
this.newLine()
}
And then use that instead:
File("somefile.txt").bufferedWriter().use { out ->
history.forEach {
out.writeLn("${it.key}, ${it.value}")
}
}
And that's how Kotlin rolls. Change things in API's to make them how you want them to be.
Wildly different flavours for this are in another answer: https://stackoverflow.com/a/35462184/3679676
Other fun variations so you can see the power of Kotlin:
A quick version by creating the string to write all at once:
File("somefile.txt").writeText(history.entries.joinToString("\n") { "${it.key}, ${it.value}" })
// or just use the toString() method without transform:
File("somefile.txt").writeText(x.entries.joinToString("\n"))
Or assuming you might do other functional things like filter lines or take only the first 100, etc. You could go this route:
File("somefile.txt").printWriter().use { out ->
history.map { "${it.key}, ${it.value}" }
.filter { ... }
.take(100)
.forEach { out.println(it) }
}
Or given an Iterable, allow writing it to a file using a transform to a string, by creating extension functions (similar to writeText() version above, but streams the content instead of materializing a big string first):
fun <T: Any> Iterable<T>.toFile(output: File, transform: (T)->String = {it.toString()}) {
output.bufferedWriter().use { out ->
this.map(transform).forEach { out.write(it); out.newLine() }
}
}
fun <T: Any> Iterable<T>.toFile(outputFilename: String, transform: (T)->String = {it.toString()}) {
this.toFile(File(outputFilename), transform)
}
used as any of these:
history.entries.toFile(File("somefile.txt")) { "${it.key}, ${it.value}" }
history.entries.toFile("somefile.txt") { "${it.key}, ${it.value}" }
or use default toString() on each item:
history.entries.toFile(File("somefile.txt"))
history.entries.toFile("somefile.txt")
Or given a File, allow filling it from an Iterable, by creating this extension function:
fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
this.bufferedWriter().use { out ->
things.map(transform).forEach { out.write(it); out.newLine() }
}
}
with usage of:
File("somefile.txt").fillWith(history.entries) { "${it.key}, ${it.value}" }
or use default toString() on each item:
File("somefile.txt").fillWith(history.entries)
which if you had the other toFile extension already, you could rewrite having one extension call the other:
fun <T: Any> File.fillWith(things: Iterable<T>, transform: (T)->String = {it.toString()}) {
things.toFile(this, transform)
}
It mostly looks ok to me. The only thing different I would do is use the "use" extension defined in ReadWrite to auto close the writer.
PrintWriter("file.txt").use {
for ((member, originalInput) in history) { // history: Map<Member, String>
it.append("$member, $originalInput\n")
}
}
At the very minimum, you could use:
FileWriter(filename).use { it.write(text) }
FileWriter is a convenience class for writing character files (provided by Java, and hence available in Kotlin). It extends Closeable, and hence can be used by Kotlin's ".use" extension method.
The .use extension method automatically closes the calling object once the block exits, thus providing an idiomatic way to close the file after it's written.
Some Kotlin magic allows to omit referencing the stream on each read or write call:
fun <T : Closeable, R> T.useWith(block: T.() -> R): R = use { with(it, block) }
File("a.in").bufferedReader().useWith {
File("a.out").printWriter().useWith {
val (a, b) = readLine()!!.split(' ').map(String::toInt)
println(a + b)
}
}
Scanner(File("b.in")).useWith {
PrintWriter("b.out").useWith {
val a = nextInt()
val b = nextInt()
println(a + b)
}
}
try{
val fileWriter = FileWriter("test.txt", true)
fileWriter.write(string+ "\n")
fileWriter.close()
} catch (exception: Exception){
println(exception.message)
}
Example as easy
val path = context!!.filesDir.absolutePath // => /data/user/0/com.example.test/files
File("$path/filename.txt").writeText("hello")
File(requireContext().filesDir, "TodayTaskListChange.txt").writeText("write your test here...")