I know Pair is immutable in Kotlin. The question is that arguments is also immutable in Kotlin. So when I got a code like this.
fun bind(pair: Pair<String, Boolean>) {
// Change pair value here
}
pair = pair.copy() is not working here. I don't know how to do.
As Pair is immutable, you can't just replace the pair value except you are using reflection. Either change Pair to something mutable or stick with it and return a new Pair instead either with copy (setting only some of the values) or with the following:
fun bind(pair: Pair<String, Boolean>) = pair.let { (left, right) -> // destructuring
// exchange pair values here... just showing a sample:
left.toInt() to right.toString() // this is basically the same as Pair(left.toInt(), right.toString())
} // in this example it returns a Pair<Int, String>
While you could probably introduce your own MutablePair-class I would rather stick to the immutability and adapt your code to handle it appropriately. Mutability can be a problem; even more so if someone else does not expect that your pair is mutable.
You can not write to the pair value itself since it's immutable, instead create a copy of the value like so:
val pair2 = pair.copy()
And use pair2 value instead of pair itself.
Also see this helpful answer.
You can use copy() to create a changed copy. (Works for every data-class.)
fun bind(pair: Pair<String, Boolean>) {
val pairCopy = pair.copy(first = "something else")
}
Then use pairCopy in the following code.
Related
Could someone explain why can't I change the value of var in that case ?
fun main(args: Array<String>) {
var number = 3
changeNumber(number)
}
fun changeNumber(number: Int) {
number = 4 //here I see a warning "val cannot be reassigned"
}
By passing a "number" to your function you "pass-by-value" NOT "pass-by-reference", the function does not know where in memory your main number is stored, therefore it cannot change it's value
you can see more about the subject here and here
There is absolutely no way to do it directly. Kotlin copies a value for scalar types (Double, Float, Boolean, Int, etc.). So any internal changes are lost.
For others types Kotlin copy a reference of parameter passed to the function. So any property/field alteration of parameter, also changes the caller parameter.
So you can wrap up your number in for this example an IntegerHolder and change the value that is kept in the reference.
data class IntegerHolder(
var v:Int
)
fun main() {
var a:IntegerHolder = IntegerHolder(2)
changeNumber(a)// Echange a value
print(a.v)
}
fun changeNumber(a:IntegerHolder) {
a.v = 5
}
Just in case you find the other answers a bit confusing, I'll add that you don't need to know about what's a scalar or passed by value. Those are under-the-hood optimizations that the compiler does but don't change the logical behavior of your code.
Kotlin works only with references, not pointers. What you're trying to do is what you can do with pointers in a language like C or C++. In those languages, you can pass a pointer to a function. A pointer is not the value of a variable, but the memory address of the variable itself so other functions can modify what the variable address is pointing at.
That's flat out not supported in Kotlin. You can only pass references. You are passing the object that the variable is pointing to, but you can't do anything to that variable itself. You are not passing a copy of that object, so if that object is mutable, you could change the values of properties inside it and the original function could see those changes by inspecting the object again. But many simple classes like Int, Float, Double, and String are all immutable, so it's logically irrelevant that you aren't passing a copy (and that's why Kotlin under-the-hood can optimize by passing actual values for some of these, called "inline classes").
A couple of workarounds for this limitation:
Mutable wrapper class. Use this in as your variable type and function parameter type.
data class IntWrapper(var value: Int)
fun main(args: Array<String>) {
val number = IntWrapper(3)
changeNumber(number)
println(number.value)
}
fun changeNumber(number: IntWrapper) {
number.value = 4
}
Pass a function that can modify your variable. The setter function is the parameter for your function that changes the variable. (The difference between pointers and what we do here is that the function that changes the variable doesn't actually know that it's changing a variable. It's just calling the function that was passed to it, which could be doing anything it wants with the provided number.)
fun main(args: Array<String>) {
var number = 3
changeNumber { number = it }
println(number)
}
fun changeNumber(numberSetter: (Int)->Unit) {
numberSetter(4)
}
But it's not very often that you'll need to do one of these. It's more common to write functions that provide a return value, and you can use that value to reassign the variable. This strategy is more robust. It provides better encapsulation, which naturally makes your code less bug-prone.
fun main(args: Array<String>) {
var number = 3
number = produceNewNumber()
println(number)
}
fun produceNewNumber(): Int {
return 4
}
I have the following code but I am convinced that it could be simpler/more elegant
package org.example
import javax.enterprise.context.ApplicationScoped
#ApplicationScoped
object CanceledRequestsHandler {
var identifiers = setOf<String>()
fun add(id: String){
var mutableIdentifiers = identifiers.toMutableList()
mutableIdentifiers.add(id)
this.ids = mutableIds.toSet()
}
}
I wanted to limit the mutability. Any suggestions t improve my code?
There is a more elegant way! Try this script file (.kts) with kotlinc (or you can run it within IDEA):
object CanceledRequestsHandler {
var ids = setOf<String>()
override fun toString(): String = ids.toString()
fun add(id: String){
ids = ids + id
}
}
System.err.println(CanceledRequestsHandler);
CanceledRequestsHandler.add("foo");
CanceledRequestsHandler.add("bar");
System.err.println(CanceledRequestsHandler);
A bit of explanation:
The + operator can be applied to collections, and works as one might expect -- returns a new collection (see https://kotlinlang.org/docs/reference/collection-plus-minus.html).
I'm not sure what your separate identifiers variable was, but you can use a single var that contains an immutable Set to do your job here.
As a commenter and the accepted answer point out, there's more you can do. If you're trying to limit mutability entirely to add(), you can lock this down further, with something like this:
object CanceledRequestsHandler {
private var _ids = mutableSetOf<String>()
val ids
get() = _ids.toSet()
override fun toString(): String = ids.toString()
fun add(id: String) {
_ids.add(id)
}
}
System.err.println(CanceledRequestsHandler);
CanceledRequestsHandler.add("foo");
CanceledRequestsHandler.add("bar");
System.err.println(CanceledRequestsHandler);
// Below line does not compile
// CanceledRequestsHandler.ids.add("baz")
Now the only way the ids property can change is via the add() method.
I wanted to limit the mutability
Converting immutable collection to mutable on each data mutation is not a limitation of mutablility, it's just overhead. The worst here is that property is declared as mutable (var). This design may lead to data loss in multi-thread case.
If data mutation is unavoidable, then it's better to have mutable (concurrent in multi-thread case) data collection with immutable property (val).
Even better way to limit mutability will be using a mutable data structure only for a short initialization period, and then freezing it into immutable (see buildSet), but I'm not sure that this approach is applicable in your case.
I am trying to use the public interface Function (as I learned it in Java) in Kotlin.
For this I created my method
fun foo(input: List<String>, modifier1: Function<List<String>>? = null){
}
as far I remember here I should be able to do modifier1.apply(input)
but seems like it is not possible (it is possible to do modifier1.apply{input} though)
Reading more about it I found this:
Kotlin: how to pass a function as parameter to another?
So I changed my method signature to this:
fun foo(input:String, modifier2: (List<String>) -> (List<String>){
}
Here I am able to do modifier2(input)
and I can call foo this way
service.foo(input, ::myModifierFunction)
where
fun myModifierFunction(input:List<String>):List<String>{
//do something
return input
}
So far this seems possible but it is not acceptable to have the function reference as nullable, is there any way I can do that? or use Function ?
You were using kotlin.Function instead of java.util.function.Function in your first example. Note that the latter takes 2 generic types: 1 for the incoming parameter and 1 for the resulting one.
The apply method you saw is the default Kotlin one: apply, not the one of Java's Function-interface.
If you really want to have the Java-function as nullable type the following should work:
fun foo(input: List<String>, modifier1: java.util.function.Function<List<String>, List<String>>? = null) {
modifier1?.apply(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Kotlin variant for the same:
fun foo(input: List<String>, modifier1: ((List<String>) -> List<String>)? = null) {
modifier1?.invoke(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Maybe also a default function, such as { it } instead of null might better suite your needs? (Java variant would be Function.identity()):
// java modifier1 : Function<List<String>, List<String>> = Function.identity()
// kotlin modifier1 : (List<String>) -> List<String> = { it }
You can make the reference nullable simply with ? — the only wrinkle is that the whole function type needs to be in parens first:
fun foo(input: String, modifier2: ((List<String>) -> List<String>)? = null) {
}
As required, modifier2 is optional; if specified, it may contain null, or it may contain a function taking and returning a list of strings.
As mentioned in another answer, kotlin.Function is not the same as java.util.function.Function — though in practice you shouldn't need to refer to either directly, as the -> notation is simpler.
If you want to pass in a function that takes List<String> as its parameter and returns nothing meaningful, the type for you is Function1<List<String>, Unit>. The method name for invoking a function is invoke(), which you could also do with just regular parentheses, if it wasn't nullable. All in all, your code could look something like this:
fun foo(input: List<String>, modifier1: Function1<List<String>, Unit>? = null) {
modifier1?.invoke(input)
}
The 1 in the typename of Function1 means that it's a one parameter function, there's also Function0, Function2, etc.
The Function type on its own is not something you can use to call that function, as it's an empty marker interface. All functions implement this regardless of how many parameters they have.
I'd like to pass a function reference on a nullable object. To take an Android example, say I want to use Activity#onBackPressed from a fragment that is a child of that actvity.
If I wanted to invoke this function, I could easily do
activity?.onBackPressed()
However, say I wanted to pass that as a reference instead:
val onBackPressedRef = activity::onBackPressed
This gives the familiar null safe error of Only safe or non null assserted calls are allowed...
I can get the error to go away with the following, but using !! is obviously not ideal:
val onBackPressedRef = activity!!::onBackPressed
Attemping activity?::onBackPressed was my first instinct, but this also breaks with several errors, where the interpreter seems confused.
val onBackPressedRef = activity?.let { it::onBackPressed }
This last variation works, but it's a lot more ugly than just using ?::. I checked all the docs I could find, but I feel like I'm missing something. Any ideas?
You are right, there is no ?:: operator in Kotlin.
You have several alternatives:
1. let and run
Thus, you have to use a helper function. Instead of let(), you can also use run(), making the expression a tiny bit shorter:
val onBackPressedRef = activity?.let { it::onBackPressed }
val onBackPressedRef = activity?.run { ::onBackPressed }
But keep in mind that either way, the invocation will be more verbose, too:
onBackPressedRef?.invoke(args)
Thus you should ask yourself, if this is really what you want, or if a no-op function call is also acceptable.
2. Closures
You could use a closure -- this will change semantics however:
val onBackPressedRef = { activity?.onBackPressed() }
Here, onBackPressedRef is not nullable anymore, so you can call it using the () operator, and in case of null activity it will have no effect.
3. Helper function
If function references with nullable objects are something you encounter a lot, you can write your own little abstraction:
// Return type: () -> Unit
fun <T> funcRef(obj: T?, function: T.() -> Unit) = { obj?.function() }
This trades a different syntax for a non-null function variable:
// activity can be null
val onBackPressedRef = funcRef(activity, Activity::onBackPressed)
// Callable directly
onBackPressedRef()
My goal: I have a simple class with a public
val reds = IntArray(10)
val greens = IntArray(10)
val blues = IntArray(10)
val lums = IntArray(10)
If someone modifies any red value, I'd like to update the lum value.
myObj.reds[5] = 100 // Should update myObj.lums[5] = reds[5]+greens[5]+blues[5]
The problems is that the by Delegates.observable seem to only be used for var objects - nothing mentions "and if you modify an element of an array, here is what gets triggered"
Maybe this isn't possible and I have to do all modifications through getters and setters - but I'd much rather have something trigger like an observable!
You will have to use a custom class instead, IntArray is mapped to primitive int[] array so it doesn't provide any place to inject callback - changing value like your example (myObj.reds[5] = 100) you only know when array is returned, but have no control over changes after that.
For example you can create class like this:
class IntArrayWrapper(size: Int,
val setterObs : ((index: Int, value: Int) -> Unit)? = null){
val field = IntArray(size)
val size
get() = field.size
operator fun iterator() = field.iterator()
operator fun get(i: Int) : Int {
return field[i]
}
operator fun set(i: Int, value: Int){
field[i] = value
setterObs?.invoke(i, value)
}
}
Operator functions will let you get values from underlying array with same syntax as if you were accessing it directly. setterObs argument in constructor lets you pass the "observer" for setter method:
val reds = IntArrayWrapper(10){index, value ->
println("$index changed to $value")
invalidateLums(index) // method that modifies lums or whatever you need
}
val a = reds[2] // getter usage
reds[3] = 5 // setter usage that triggers the setter observer callback
reds.field[4] = 3 // set value in backing array directly, allows modification without setter callback
Note that this imposes limitations, as you won't be able to freely use IntArray extension methods without referencing backing field nor will you be able to pass this class as an Array argument.
I do not know if it is the cleanest way of solving your problem but, you could use the ObservableList (FX observable collections):
var numbers: ObservableList<Int> = FXCollections.observableArrayList()
numbers.addListener(ListChangeListener<Int> {
//Do things on change
})
But as I mentioned, by adding these collections you are mixing FX components into your application, which I do not know if it is wanted or even if it works on various platforms like android!