Is it Ok to use non-local state from a Composable function? An example would be a Composable that shows a Text with a String taken from a MutableState stored as a member of an object retrieved through an Ambient, like this:
data class ServiceX (
val whateverString: MutableState<String>("meow")
)
#Composable
fun Whatever() {
val serviceX = AmbientServiceX.current
Text(serviceX.whateverString)
}
Will the Composable function repaint when whateverString changes? Are there any problems with this?
It should technically work, But you'd probably want to change MutableState to mutableStateOf which does some more compose goodies under the hood.
But I'd suggest avoiding patterns like this in compose. Ambients in general should be used rarely, as ambients make composables 'magic' with it being non obvious where the value came from or where a value change was triggered. It essentially makes your code very hard to debug.
Lean on the side of creating isolated components as these are way simpler to build and maintain - and are the big benefit of compose.
#Composable
fun Whatever(whateverString: String) {
Text(whateverString)
}
Related
Kotlin coroutines and Arrow are a nice way to avoid nesting flatmaps, introducing monadic comprehensions in Kotlin. However Kotlin's Flow type still relies on declarative flatmapping, so we get into a mixture of direct and declarative styles:
override suspend fun findAll(page: Pageable): Either<BusinessException, Flow<PageElement<ClientOut>>> = either {
val count = clientRepository.count().awaitSingle().bind()
return clientRepository.findByIdNotNull(page).asFlow()
.flatMapMerge { client ->
flow { emit(mapDetailedClientOut(client)) }
}
}
val count has been bound inside the either {...} comprehension. However, there doesn't seem to be a way to do the same with Flow, forcing us to nest a flatmapMerge().
Is there a way to do it, or is it planned to be somehow included in the near future?
Sadly there is currently no way to build comphrehensions for the KotlinX Flow datatype, since Coroutines in Kotlin only support for single-shot emission/bind.
Therefore it's only possible to build comphrensions for data types with 0..1 elements such as Either or Nullable, but not 0..N like the Flow or List data types.
I wonder if a data class with one of the properties being a function, such as:
data class Holder(val x: Data, val f: () -> Unit)
can work at all, since the following test fails.
val a = {}
val b = {}
Assert.assertEquals(a, b)
Update: Use case for this could be to have a
data class ButtonDescriptor(val text: String, val onClick: () -> Unit)
and then flow it to UI whilst doing distinctUntilChanged()
I don't think this is possible, I'm afraid.
You can of course check reference equality (===, or == in this case because functions don't generally override equals()). That would give you a definite answer where you have references to the same function instance. But that doesn't check structural equality, and so reports the two lambdas in the question as different.
You can check whether the two functions are instances of the same class by checking their .javaClass property. If the same, that would imply that they do the same processing — though I think they could still have different variables/captures. However, if different, that wouldn't tell you anything. Even the simple examples in the question are different classes…
And of course, you can't check them as ‘black boxes’ — it's not feasible to try every possible input and check their outputs. (Even assuming they were pure functions with no side effects, which in general isn't true!)
You might be able to get their bytecode from a classloader, and compare that, but I really wouldn't recommend it — it'd be a lot of unnecessary work, you'd have to allow for the difference in class names etc., it would probably have a lot of false negatives, and again I think it could return the same code for two functions which behaved differently due to different parameters/captures.
So no, I don't think this is possible in JVM languages.
What are you trying to achieve with this, and could there be another way? (For example, if these functions are under your control, can you arrange for reference equality to do what you need? Or could you use function objects with an extra property giving an ID or something else you could compare?)
When you create your data class, if you pass the function by reference it will work with DiffUtils and distinctUntilChanged(). Function references do not break the isEquals() method of data classes in the same way that a lambda does.
For example, you create a function for your onClick:
private fun onClick() { // handle click }
and create your data class like
BottomDescriptor("some text", ::onClick)
Using getters and setters is a very well known practice in object oriented languages. This is done in order to have a greater control on the variables. To achieve this, we make the variables private in java and hence we need both getters and setters there.
But in kotlin this is not the case. Here even public variables are accessed through getters and setters by default. Though setters can be used to validate an assignment to a variable, getters just return the variable as it is stored (and I think this is it for them). Hence custom getters are not required at all.
I have also seen some wrong usage of this feature where instead of writing a zero argument function, they use a val and do the computation in the getter. This creates an illusion that the thing is just a val but in reality it does not store anything and instead it performs a computation every time.
So is there a real need to have a custom getter?
getters just return the variable as it is stored (and I think this is it for them). Hence custom getters are not required at all.
If that was really the case, why have getters at all in Java? One of the goals of encapsulation is to make sure a change in the class doesn't change it's API. It's the same in Kotlin.
I have also seen some wrong usage of this feature where instead of writing a zero argument function, they use a val and do the computation in the getter. This creates an illusion that the thing is just a val but in reality it does not store anything and instead it performs a computation every time.
This is a perfectly valid use case for a custom getter. In Kotlin, one must not assume that using a property is entirely free of overhead. There are many questions to ask yourself when choosing between a property with a getter or a zero-arg function:
Does it describe behavior? Use a function (walk(), build(), etc)
Does it describe state? Use a property (firstName, lastIndex, etc)
Additionally, a property getter should not throw an exception, should be either cheap to calculate or cached on first access, and should return the same result for multiple consecutive executions. Here's examples from the standard library:
ArrayDeque.first() is a function, it throws if deque is empty.
List.lastIndex is a property, it's cheap to calculate.
Lazy<T>.value is a property, the value is computed and cached on first access.
Most delegated properties make use of custom getters.
More reading:
Why use getters and setters/accessors?
Kotlin: should I define Function or Property?
Just some more info. Other than readability, the possibility of defining a custom getter allows you to evolve a class without changing its public members, even if you started with a simple val with no custom getter.
In a language without properties like Java, if you define a public field:
public class Foo {
public final int value;
public Foo(int value) {
this.value = value;
}
}
And then later you want to modify the class to add a feature where it returns negated values if you flip a Boolean, there's no way to do it without breaking code that uses the original version of the class. So you should have used getters and setters to begin with.
But in Kotlin, you can't directly expose a backing field like this, so it's impossible to paint yourself in a corner like you could with a public field in Java. If your original class is like this:
class Foo(val value: Int)
You could modify it like this to add the feature and have no impact on code that already uses the class.
class Foo(private val originalValue: Int) {
var isNegated = false
val value: Int
get() = if (isNegated) -originalValue else originalValue
}
I have a class Track which holds a set of Points and represent person location in time. In order to get this result I run an iterative optimization routine combining different data. In order to do it I extend Point with a class OptimizedPoint which holds data for optimization for this point and current value. I also introduce OptimizedTrack which is a collection of OptimizedPoints and additional data needed for optimization associated with the whole track.
I run an optimization on OptimizedTrack and at the last iteration, I return to the user clean data (Track) which only has the result and doesn't have additional data. However, I can not find a way to express with OOP that OptimizedTrack is somehow an extension of the Track and introduce common routines for them. F.e getting a length of the track which should be available for both of them as it only uses data which can be found both in OptimizedTrack and Track
Right now I have such architecture Point{x, y, z}, OptimizedPoint extends Point {additional_data}. Track {array<Point>}, OptimizedTrack {array<OptimizedPoint>, additional_data}. I don't understand how to express that OptimizedTrack is an extension of Track as I can not express array<OptimizedPoint> extens array<Point>. So I can not introduce a common routine length which can be calculated for array and therefore also from array.
I do not insist on my current architecture. It's most probably wrong, I only write it here to express the problem I am facing. How would you propose to refactor my architecture in this case?
I believe that the basic premise of what you are trying to do is faulty if you are following what is considered to be proper use of inheritance to express subtyping relationships.
Inheritance can be used for various purposes and I am not wishing to pontificate upon the subject, but the opinion of most authorities is that inheritance is best and most safely used when used for subtyping. In short, an instance of a subclass should be able to be substituted for an instance of its base class without "breaking" the program (see: Liskov Substitution Principle).
Let us assume that OptimizedPoint is a subtype of Point. Then all the methods defined in class Point when invoked on an instance of OptimizedPoint will continue to function as expected. That means that OptimizedPoint cannot require any more stringent preconditions on any of these method invocations nor can it weaken any of the promissed postconditions that the contract Point has made.
But it is a common fallacy that just becuase OptimizedPoint is a subtype of Point that a container of OptimizedPoint, i.e. OptimizedTrack, is a subtype of a container of Point, i.e Track. This is because you cannot substitute an instance of OptimizedTrack for an instance of Track (due to your not being able to add an instance of Point to an instance of OptimizedTrack).
So, if you are trying to follow "good object-oriented design principles", it is disastrous trying to somehow make OptimizedTrack a subclass of Track, because it can certainly never be a subtype. You can, of course, reuse Track to build OptimizedTrack using composition, i.e. OptimizedTrack would contain within an instance of Track to which methods such as length would be delegated.
I'm not sure why you want to return a Track to your client code after the optimisation process, considering that OptimizedTrack is a Track itself. Below is a quick example of what I think you're trying to achieve (written in Kotlin because is less verbose).
You can achieve a lot more flexibility and solve the type issue if you consider Track to be an iterable object of points of type Point. This way, when you extend OptTrack from Track, you will be able to:
Substitute Track and OptTrack with no problem (even if your optimised track object has not computed a simplified Track object).
Simplify through optimize and return a Track from OptTrack with no issues (the optimize function on Point is irrelevant, you can return an OptPoint inside your Track because it extends the object Point)
open class Point(val x: Int, val y: Int, val z: Int) {
override fun toString(): String =
"Point(${this.x}, ${this.y}, ${this.z})"
}
data class OptPoint(val point: Point, val additional: Int):
Point(point.x, point.y, point.z) {
override fun toString(): String =
"OptPoint(${this.point}, ${this.additional})"
fun optimize(): Point {
return Point(this.x, this.y, this.z)
}
}
open class Track(private val points: Iterable<Point>): Iterable<Point> {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
override fun toString(): String =
"Track(${this.points})"
}
data class OptTrack(private val points: Iterable<OptPoint>): Track(listOf()) {
override operator fun iterator(): Iterator<Point> {
return this.points.iterator()
}
fun optimize(): Track {
return Track(this.points.map{ it.optimize() })
}
}
fun main(args: Array<String>) {
val track: Track = OptTrack(listOf(
OptPoint(Point(1, 2, 3), 4))).optimize()
println(track)
// Track([Point(1, 2, 3)])
val other: Track = OptTrack(listOf(OptPoint(Point(1, 2, 3), 4)))
println(other)
// OptTrack(points=[OptPoint(Point(1, 2, 3), 4)])
}
In OOP you should prefer object composition to object inheritance. In your problem, I think creating interfaces for point and track could help. In order to achieve the proper result, I think, you should create two interfaces, IPoint & ITrack. Both Track and OptimizedTrack implement the ITrack interface and for common operations, you could create another class that both classes delegate the requests to it. After that you could create an strategy class, taking in an ITrack and returns another optimized ITrack. In the ITrack you could create GetPath which returns a list of objects of type IPoint.
I asked a question at How to design a complex class which incude some classes to make expansion easily in future in Kotlin? about how to design a complex class which incude some classes to make expansion easily in future in Kotlin.
A expert named s1m0nw1 give me a great answer as the following code.
But I don't know why he want to change MutableList to List at https://stackoverflow.com/posts/47960036/revisions , I can get the correct result when I use MutableList. Could you tell me?
The code
interface DeviceDef
data class BluetoothDef(val Status: Boolean = false) : DeviceDef
data class WiFiDef(val Name: String, val Status: Boolean = false) : DeviceDef
data class ScreenDef(val Name: String, val size: Long) : DeviceDef
class MDetail(val _id: Long, val devices: List<DeviceDef>) {
inline fun <reified T> getDevice(): T {
return devices.filterIsInstance(T::class.java).first()
}
}
Added
I think that mutableListOf<DeviceDef> is better than ListOf<DeviceDef> in order to extend in future.
I can use aMutableList.add() function to extend when I append new element of mutableListOf<DeviceDef>.
If I use ListOf<DeviceDef>, I have to construct it with listOf(mBluetoothDef1, mWiFiDef1, //mOther), it's not good. Right?
var aMutableList= mutableListOf<DeviceDef>()
var mBluetoothDef1= BluetoothDef(true)
var mWiFiDef1= WiFiHelper(this).getWiFiDefFromSystem()
aMutableList.add(mBluetoothDef1)
aMutableList.add(mWiFiDef1)
// aMutableList.add(mOther) //This is extension
var aMDetail1= MDetail(myID, aMutableList)
Sorry for not giving an explanation in the first place. The differences are explained in the docs.:
Unlike many languages, Kotlin distinguishes between mutable and immutable collections (lists, sets, maps, etc). Precise control over exactly when collections can be edited is useful for eliminating bugs, and for designing good APIs.
It is important to understand up front the difference between a read-only view of a mutable collection, and an actually immutable collection. Both are easy to create, but the type system doesn't express the difference, so keeping track of that (if it's relevant) is up to you.
The Kotlin List<out T> type is an interface that provides read-only operations like size, get and so on. Like in Java, it inherits from Collection<T> and that in turn inherits from Iterable<T>. Methods that change the list are added by the MutableList<T> interface. [...]
The List interface provides a read-only view so that you cannot e.g add new elements to the list which has many advantages for instance in multithreaded environments. There may be situations in which you will use MutableList instead.
I also recommend the following discussion:
Kotlin and Immutable Collections?
EDIT (added content):
You can do this is a one-liner without any add invocation:
val list = listOf(mBluetoothDef1, mWiFiDef1)