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.
Related
I wrote an extension function to get an element of an JSON object by its name:
fun JSONObject.obj (name: String): JSONObject? =
try { this.getJSONObject(name) }
catch (e: JSONException) { null }
Now I want to extend this for nested JSON objects. I wrote the following:
tailrec fun JSONObject.obj (first: String, vararg rest: String): JSONObject? =
if (rest.size == 0)
obj(first)
else
obj(first)?.obj(rest[0], *rest.drop(1).toTypedArray())
But this looks quite inefficient to me.
What is the best way to slice a vararg argument?
We could use vararg only in the public function, but then internally use list for recursion:
fun JSONObject.obj (first: String, vararg rest: String): JSONObject? = obj(first, rest.asList())
private tailrec fun JSONObject.obj (first: String, rest: List<String>): JSONObject? =
if (rest.size == 0)
obj(first)
else
obj(first)?.obj(rest[0], rest.subList(1, rest.size))
Both asList() and subList() don't copy data, but only wrap the existing collection. Still, this is far from ideal, because it creates a new object for each iteration and it may create a chain of views (it depends on internal implementation of subList()). Alternatively, the internal function could receive an array and offset - this will solve both above problems.
Generally, I suggest to not try turning Kotlin into something it is not. It has limited support for functional constructs, but it is not a functional language. Without the linked list implementation which could be easily split into head and tail, this style of code will be always inefficient and/or cumbersome. You can look for such implementation, for example in Arrow or kotlinx.collections.immutable. The latter has ImmutableList with optimized subList() - you can use it with the solution provided above to avoid creating a chain of lists.
Update
As a matter of fact, basic lists implementations in the Java stdlib also provide optimized subList(): AbstractList.java. Therefore, the above solution using simply asList() should be fine, at least when targeting JVM.
Instead of slicing, why don't you try just iterating over all the objects and getting the JSONObjects? I think this would be much more efficient.
fun JSONObject.obj(vararg names: String): JSONObject? {
var jsonObject = this
for (name in names) {
if (!jsonObject.has(name))
return null
jsonObject = jsonObject.getJSONObject(name)
}
return jsonObject
}
I was following a tutorial for learning kotlin and ran into this example.
open class AquariumPlant(val color: String, private val size: Int)
class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)
fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")
val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print() // what will it print?
Well this apparently prints "Aquarium Plant" instead of "GreenLeafyPlant". I was a bit confused by this so I tested this out with this little snippet of code.
open class Aquarium {
open fun printSize() {
println("hello")
}
}
class TowerTank: Aquarium() {
override fun printSize() {
println("rawr")
}
}
fun main() {
towerTank = TowerTank()
(towerTank as Aquarium).printSize()
}
So this prints "rawr" and not "hello". My question is why doesn't it print "hello"? Aren't these two examples contradicting themselves? How does the function extensions create this difference in behaviour? Sorry if this may seem like a dumb question, I'm new to Kotlin as you can probably tell.
To understand this we need to understand how extensions work. Extensions don't magically add new members to existing classes. This is technically impossible both in Java and Kotlin. Instead, they work as good old static utility functions in Java. Accessing them as members is just a syntactic sugar.
First example is really similar to these functions:
fun print(plant: AquariumPlant) = println("AquariumPlant")
fun print(plant: GreenLeafyPlant) = println("GreenLeafyPlant")
To make it even more clear, we can rename these functions:
fun printAquariumPlant(plant: AquariumPlant) = println("AquariumPlant")
fun printGreenLeafyPlant(plant: GreenLeafyPlant) = println("GreenLeafyPlant")
Now, it is pretty clear that if we have object like this:
val aquariumPlant: AquariumPlant = GreenLeafyPlant(size = 10)
Then we can only invoke printAquariumPlant() function with it and it will print AquariumPlant, not GreenLeafyPlant. Despite the fact aquariumPlant is actually a GreenLeafyPlant object.
If we move one step back and rename them again to just print, nothing will really change. aquariumPlant variable is of type AquariumPlant (even if it contains GreenLeafyPlant object), so the compiler chooses print(AquariumPlant) function.
This is why we say extensions are resolved statically. Compiler decides which function to call at compile time. Virtual functions are resolved at runtime, taking into consideration the real type of the object.
Say I have a data class
data class MyClass(val crop: Rect, val name: String)
But I want to make a copy of the Rect passed in since I don't want the value to be modified later. I don't want to the caller to call
MyClass(Rect(inCrop), "name")
in the code. How can I do this in my data class?
Thanks.
One workaround I can think of is:
data class MyClass(private var privateCrop: Rect, val name: String) {
val crop get() = privateCrop
init {
privateCrop = Rect(privateCrop)
}
}
You make crop private and make it a var (privateCrop), then you add a public getter for it. Now you can copy it in an init block.
But I gotta admit, this is rather ugly. The better solution here I think is to change Rect to be immutable, but if Rect isn't in your control, then I guess it can't be helped. You might also consider using a regular class.
You may not want to alter data class's like this. As per another solution's answer, you may find other peculiarities with this solution. The solution given by #Sweeper, also does not include providing a defensive copy, which you may want to do to avoid access to modifying the internal property field.
To quote:
After spending almost a full year of writing Kotlin daily I've found that attempting to override data classes like this is a bad practice. There are 3 valid approaches to this, and after I present them, I'll explain why the approach other answers have suggested is bad.
Have your business logic that creates the data class alter the value to be 0 or greater before calling the constructor with the bad value. This is probably the best approach for most cases.
Don't use a data class. Use a regular class and have your IDE generate the equals and hashCode methods for you (or don't, if you don't need them). Yes, you'll have to re-generate it if any of the properties are changed on the object, but you are left with total control of the object.
class Test(value: Int) {
val value: Int = value
get() = if (field < 0) 0 else field
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Test) return false
return true
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
}
Create an additional safe property on the object that does what you want instead of having a private value that's effectively overriden.
I am developing a simple Android app, that will display an icon of a vehicle and the user can click on the icon to display the vehicle information. I want to load the data dynamically when I build the app i.e. the data will come from an external source including the picture for the icon.
I am new to Kotlin and not sure what to search for to understand a suitable solution. What is the correct way to define the data, is it best to create an class as below then create an array of the class (not sure if this is possible)
public class VehicleSpec()
{
var OEM: String? = null
var ModelName: String? = null
var EngineSize: String? = null
}
Or would be better to create a multiple dimension array and then link the data to the cells?
var VehicleSpec = arrayOf(20,20)
VehicleSpec[0][0] = Null //OEM
VehicleSpec[0][1] = Null //ModelName
VehicleSpec[0][2] = Null //EngineSize
What is the best way to set up the data storage, is there any good references to understand how this should be setup?
What is the correct way to define the data, is it best to create an class as below then create an array of the class
Using an array for the properties of an object is not making the full use of the type safety you have in Kotlin (and even Java for that matter).
If what you want to express is multiple properties of an object, then you should use a class to define those properties. This is especially true if the properties have different types.
There is no performance difference between an array and a class, because you'll get a reference to the heap in both cases. You could save on performance only if you convert your multi-dimensional array approach to a single-dimension array with smart indexing. Most of the time, you should not consider this option unless you are handling a lot of data and if you know that performance is an issue at this specific level.
(not sure if this is possible)
Defining lists/arrays of classes is definitely possible.
Usually, for classes that are only used as data containers, you should prefer data classes, because they give you useful methods for free, and these methods totally make sense for simple "data bags" like in your case (equals, hashcode, component access, etc.).
data class Vehicle(
val OEM: String,
val ModelName: String,
val EngineSize: String
)
Also, I suggest using val instead of var as much as possible. Immutability is more idiomatic in Kotlin.
Last but not least, prefer non-null values to null values if you know a value must always be present. If there are valid cases where the value is absent, you should use null instead of a placeholder value like empty string or -1.
First at all, using the "class aprocah" makes it easy for you to understand and give you the full benefits of the language itself... so dont dry to save data in an array .. let the compiler handle those stuff.
Secondly i suggest you have maybe two types (and use data classes ;-) )
data class VehicleListEntry(
val id: Long,
val name: String
)
and
data class VehicleSpec(
val id: Long,
val oem: String = "",
val modelName: String = "",
val engineSize: String = ""
)
from my perspective try to avoid null values whenever possible.
So if you have strings - which you are display only - use empty strings instead of null.
and now have a Model to store your data
class VehicleModel() {
private val specs: MutableMap<Long, VehicleSpec> = mutableMapOf()
private var entries: List<VehicleListEntry> = listOf()
fun getSpec(id: Long) = specs[id]
fun addSpec(spec: VehicleSpec) = specs[spec.id] = spec
fun getEntries(): List<VehicleListEntry> = entries
fun setEntries(data: List<VehicleListEntry>) {
entries = data.toMutableList()
}
}
You could also use a data class for your model which looks like
data class VehicleModel(
val specs: MutableMap<Long, VehicleSpec> = mutableMapOf(),
var entries: List<VehicleListEntry> = listOf()
)
And last but not least a controller for getting stuff together
class VehicleController() {
private val model = VehicleModel()
init{
// TODO get the entries list together
}
fun getEntries() = model.entries
fun getSpec(id: Long) : VehicleSpec? {
// TODO load the data from external source (or check the model first)
// TODO store the data into the model
// TODO return result
}
}
I have a custom getter method for a mutable list to return an immtuable list by using Google's Guava library. And then this mutable list is accessed in the constructor.
data class mutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = mutableListOf()
list.forEach {
mutableList.add(it.copy()) // Exception is thrown here.
// It actually calls its getter method which is an immutable
// list, so when init this class, it throw exception
}
}
}
data class Foo {}
And I decompile it to Java, in the init block, it calls the getter method of mutableList.
Is there a way to call the mutabbleList itself instead of getter method?
Of course it calls the getter (which returns ImmutableList.copyOf(field)).
You can do simply assignment to mutableList new copied mutable list in your init block:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = list.map { it.copy() }.toMutableList()
}
}
or whithout init:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
get() = ImmutableList.copyOf(field)
}
Kotlin stdlib opts for interface immutability. That means, the interface an implementation is boxed in determines the mutability of the reference itself.
Therefore, the right way to make a MutableList<T> just a List<T> is to box it, like follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList as List<Int>
That way, being the myImmutableList reference boxed in a List<Int>, it will only expose members from List<Int>, and not those that just MutableList<Int> define, which allow to mutate the state of the object, hence the list.
Then, if you really want to avoid the following issue (resuming from the above code),
val hackedList = myImmutableList as MutableList<Int>
... for which you would be able to access the mutable implementation through unboxing, you may rather be opting for the following solution:
class ImmutableList<T>(list: MutableList<T>) : List<T> by list
fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
And then use it as follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList.toImmutable()
So you'll be avoiding the issue above. Indeed, any attempt to unbox the value return from MutableList<T>.toImmutable() will end up with a TypeCastException, as the implementation of the List<T> is no longer a MutableList<T>. Rather, it is an ImmutableList<T>, which doesn't expose any methods that might mutate the object.
Unlike #Lucas method, this way you won't be wasting time to copy elements, as you'll be relying on the by keyword in Kotlin, which allows you to implement an interface through an already existing implementation. That is, the MutableList<T> you'll be passing to the constructor of ImmutableList<T>.
When I was researching about this topic, the best solution it just worked for me is just enforcing by contract. If you are creating a mutable list, let's say:
val immutableList = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
).toList() // We make it immutable?
and then you use an extension function or any of the recommendations given below, like using ImmutableList.copyOf(field), you might be paying a penalty because you're copying the items into another collection.
Another option is just paying the unboxing cost of doing something like:
val myImmutableList = myMutableList as List<Int>
The solution I opted for is just enforcing by contract, it's a very simple concept. Your MutableList inherits from List. If you want to share a collection of items with that level of abstraction, it's your choice to do it by enforcing the type:
val immutableList: List<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
now if we share that list with another component, we'll use the right abstraction without any cost. We could also have used a Collection, because List inherits from Collection:
val immutableList: Collection<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
For me, using a var instead of val field along with a private setter usually works best
class Order
class Something() {
var orders: List<Order> = listOf()
private set
fun addOrder(order: Order) {
orders = orders
.toMutableList()
.apply { add(order) }
}
}
This exposes it as immutable and requires a single field only. The price we pay is the overhead of creating a new collection when adding elements it.