Seems so simple, but, how do I initialize Kotlin's MutableList to empty MutableList?
I could hack it this way, but I'm sure there is something easier available:
var pusta: List<Kolory> = emptyList()
var cos: MutableList<Kolory> = pusta.toArrayList()
You can simply write:
val mutableList = mutableListOf<Kolory>()
This is the most idiomatic way.
Alternative ways are
val mutableList : MutableList<Kolory> = arrayListOf()
or
val mutableList : MutableList<Kolory> = ArrayList()
This is exploiting the fact that java types like ArrayList are implicitly implementing the type MutableList via a compiler trick.
Various forms depending on type of List, for Array List:
val myList = mutableListOf<Kolory>()
// or more specifically use the helper for a specific list type
val myList = arrayListOf<Kolory>()
For LinkedList:
val myList = linkedListOf<Kolory>()
// same as
val myList: MutableList<Kolory> = linkedListOf()
For other list types, will be assumed Mutable if you construct them directly:
val myList = ArrayList<Kolory>()
// or
val myList = LinkedList<Kolory>()
This holds true for anything implementing the List interface (i.e. other collections libraries).
No need to repeat the type on the left side if the list is already Mutable. Or only if you want to treat them as read-only, for example:
val myList: List<Kolory> = ArrayList()
I do like below to :
var book: MutableList<Books> = mutableListOf()
/** Returns a new [MutableList] with the given elements. */
public fun <T> mutableListOf(vararg elements: T): MutableList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
Create Mutable list of nullable String in kotlin
val systemUsers: MutableList<String?> = mutableListOf()
It is absolutely valid to use the MutableList() function of the Kotlin collections that intentionally looks like a constructor. This function is in general very useful to know because it can also consume an initialization function that pre-fills all values of a (non-empty) list.
val emptyListOfTypeUnit = MutableList(0) {}
val emptyListOfTypeInt = MutableList(0) { 0 }
val verboseEmptyListOfTypeInt = MutableList<Int>(0) { 0 }
val emptyListOfTypeString = MutableList(0) { "" }
val verboseEmptyListOfTypeString = MutableList<String>(0) { "" }
val emptyListOfTypeKolory = MutableList(0) { Kolory() }
val verboseEmptyListOfTypeKolory = MutableList<Kolory>(0) { Kolory() }
Disclaimer: I was introduced to this in the Jetbrains Academy course for Kotlin developers, which is unfortunately not public. Therefore, I cannot link a reference here. Sorry.
Related
I try to put a key-value to ConcurrentHashMap in Kotlin buf failed. The compiler tells me: No set method providing array access.
class MysqlDataProviderProxy() {
private val NULL: Any = Object()
var unionMaps: Map<Long, Any> = ConcurrentHashMap()
fun init() {
unionMaps[1] = NULL // No set method providing array access
}
}
I don't know what does it mean. Is ConcurrentHashMap in Kotlin unmutable?
As Sweeper says you have the wrong type on unionMap.
Is ConcurrentHashMap in Kotlin unmutable
No, but unlike Java Maps/Lists/Sets have mutable and immutable interfaces and the interfaces Map, List, Set are the immutable variation.
You want this:
class MysqlDataProviderProxy() {
private val NULL: Any = Object()
var unionMaps: MutableMap<Long, Any> = ConcurrentHashMap()
init {
unionMaps[1] = NULL
}
}
And the other thing is that your original function init() does not get executed as the instance is constructed, you probably want init {... as I show above
but the problem with that approach is that now you have made the map mutable outside your proxy class which might not be your intention, in which case you could do this:
class MysqlDataProviderProxy() {
private val NULL: Any = Object()
var unionMaps: Map<Long, Any> = ConcurrentHashMap()
init {
(unionMaps as MutableMap)[1] = NULL
}
}
Suppose that there a class A with property list:
class A {
val list = mutableListOf<String>()
fun addAText() {
list.add("a text")
}
}
but now not only method addAText() can add a string to the list but also everyone from everywhere.
So I solve this rewriting above code to this:
class A {
private val mutList = mutableListOf<String>()
val list: List<String>
get() = mutList
fun addAText() {
mutList.add("a text")
}
}
So question is: Is there a better way? like any syntax support or standard kotlin lib?
Additional private properties seems to be not elegant solution.
That is the best approach and there is a convention using underscore. Repeat the same naming but prepend underscore for the private.
private val _list = mutableListOf<String>
val list: List<String>
get() = _list
Is part of the naming conventio known as backing properties
Is very usefull for live data
private val _liveText = MutableLiveData("default")
val liveText: LiveData<String>
get() = _liveText
fun update(text: String) {
_liveText.value = text
}
So in a ViewModel you expose the observable but encapsulate the mutable.
how can I set properties of a dataclass by its name. For example, I have a raw HTTP GET response
propA=valueA
propB=valueB
and a data class in Kotlin
data class Test(var propA: String = "", var propB: String = ""){}
in my code i have an function that splits the response to a key value array
val test: Test = Test()
rawResp?.split('\n')?.forEach { item: String ->
run {
val keyValue = item.split('=')
TODO
}
}
In JavaScript I can do the following
response.split('\n').forEach(item => {
let keyValue = item.split('=');
this.test[keyValue[0]] = keyValue[1];
});
Is there a similar way in Kotlin?
You cannot readily do this in Kotlin the same way you would in JavaScript (unless you are prepared to handle reflection yourself), but there is a possibility of using a Kotlin feature called Delegated Properties (particularly, a use case Storing Properties in a Map of that feature).
Here is an example specific to code in your original question:
class Test(private val map: Map<String, String>) {
val propA: String by map
val propB: String by map
override fun toString() = "${javaClass.simpleName}(propA=$propA,propB=$propB)"
}
fun main() {
val rawResp: String? = """
propA=valueA
propB=valueB
""".trimIndent()
val props = rawResp?.split('\n')?.map { item ->
val (key, value) = item.split('=')
key to value
}?.toMap() ?: emptyMap()
val test = Test(props)
println("Property 'propA' of test is: ${test.propA}")
println("Or using toString: $test")
}
This outputs:
Property 'propA' of test is: valueA
Or using toString: Test(propA=valueA,propB=valueB)
Unfortunately, you cannot use data classes with property delegation the way you would expect, so you have to 'pay the price' and define the overridden methods (toString, equals, hashCode) on your own if you need them.
By the question, it was not clear for me if each line represents a Test instance or not. So
If not.
fun parse(rawResp: String): Test = rawResp.split("\n").flatMap { it.split("=") }.let { Test(it[0], it[1]) }
If yes.
fun parse(rawResp: String): List<Test> = rawResp.split("\n").map { it.split("=") }.map { Test(it[0], it[1]) }
For null safe alternative you can use nullableString.orEmpty()...
Assume we have a custom collection
class CopyOnWriteCollection<T> {
// returns copy of collection with new element
fun add(element: T): CopyOnWriteCollection<T> {
...
}
}
if i need to add several elements i would do something like this:
val newCollection = oldCollection
.add(1)
.add(2)
.add(3)
And newCollection contains elements from oldCollection and also contains 1,2,3.
Perfect!
But how can i add elements from another collection using forEach of map?
val collection = CopyOnWriteCollection()
(1..3).forEach { collection.add(it) } // this approach works only with mutable collections
You can use an imperative loop, or you can use the fold()function:
fun main() {
var collection = CopyOnWriteCollection<Int>()
var collection2 = collection
for (i in 1..3) {
collection = collection.add(i)
}
println(collection)
collection2 = (1..3).fold(collection2) { coll, i -> coll.add(i) }
println(collection2)
}
class CopyOnWriteCollection<T> {
private val list = mutableListOf<T>()
// returns copy of collection with new element
fun add(element: T): CopyOnWriteCollection<T> {
val copy = CopyOnWriteCollection<T>()
copy.list.addAll(this.list)
copy.list.add(element)
return copy;
}
override fun toString() = list.toString()
}
If the CopyOnWriteCollection class is under your control, I'd approach this by adding an addAll() method to it:
/** Returns copy of collection with new element. */
fun addAll(elements: Collection<T>): CopyOnWriteCollection<T> {
// ...
}
You could then call that with e.g.
val newCollection = oldCollection.addAll(listOf(1, 2, 3))
(Or you could take a vararg instead of a collection.)
That's likely to take a lot less time and memory.
(Also, if you really need to write your own collection class, I'd strongly recommend implementing Collection (or one of its subinterfaces if appropriate), or extending a class which does. That will give access to the huge range of extension methods and other goodies in the stdlib.)
Let's take the class of a data class:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
)
When calling this function from Kotlin, it is pretty straightforward. I can simply use the named-argument syntax to do so. Calling from Java, I have to specify all values, or use the #JvmOverloads annotation, which generates the following constructors (in addition to the constructor that kotlin generates with the bit-mask for default values):
User(int userNumber, #NotNull String name, #NotNull List userGroups,
#NotNull String screenName)
User(int userNumber, #NotNull String name, #NotNull List userGroups)
User(int userNumber, #NotNull String name)
User(#NotNull String name)
Now, if I want to create a User object in Java equivalent to User(name="John Doe", userGroups=listOf("admin", "super") I can't do it with the above constructors. I CAN however do it if I put val userNumber: Int = -1 at the end in the data class declaration (the generation of constructors seems to depend on the order the optional arguments are defined in). Which is fine, because expecting kotlin to generate all permutations is going to heavily bloat some classes.
The biggest problem that tools like Jackson simply don't work as they have no idea which constructor to use (and not like I can annotate one of the generated ones specially).
So, is there a way to generate a (single) constructor like:
User(Integer userNumber, String name, List<String> userGroups, String screenName) {
this.userNumber = (userNumber == null) ? -1 : userNumber;
this.userGroups = (userGroups == null) ? Collections.emptyList() : userGroups;
//...
}
Currently I am using the above approach, but manually defining the constructors where I need them.
EDIT
I should clarify, creating a similar constructor doesn't work, obviously because both the signatures would clash on the JVM. This is what it would like in my case:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups; List<String> = emptyList(),
val screenName: String = "new-user"
) {
companion object {
#JvmStatic
#JsonCreator
fun constructionSupport(
#JsonProperty("userNumber") userNumber : Int?,
#JsonProperty("name") name : String,
#JsonProperty("userGroups") userGroups : List<String>?,
#JsonProperty("screenName") screenName : String?
) = User(
userNumber = userNumber ?: -1,
name = name,
userGroups = userGroups ?: emptyList(),
screenName = screenName ?: "new-user"
)
}
}
Also note the redundancy where I have to write the default values for the properties twice. I Now that I look at it, I doubt there exists a solution for this. Maybe this is a good use-case for a kapt based side-project of mine :)
Better solution is to add possibility to library understand Kotlin functional. For example, for Jackson exists jackson-module-kotlin. With this library we can use default arguments in data classes.
Example:
data class User(
val userNumber: Int = -1,
val name: String,
val userGroups: List<String> = emptyList(),
val screenName: String = "new-user"
)
fun main(args: Array<String>) {
val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
val testUser = User(userNumber = 5, name = "someName")
val stringUser = objectMapper.writeValueAsString(testUser)
println(stringUser)
val parsedUser = objectMapper.readValue<User>(stringUser)
println(parsedUser)
assert(testUser == parsedUser) {
println("something goes wrong")
}
}
After kicking this around for a minute, I think I found a solution that may work well here. Simply define a top level function in the same source file, that will build the object. Perhaps like so:
fun build_user(userNumber: Int?, name: String, userGroups: List<String>?, screenName: String?) : User {
return User(if(userNumber !== null) userNumber else -1, name, if(userGroups !== null) userGroups else emptyList(),
if(screenName !== null) screenName else "new-user")
}
Then when you need it, you simply call it from Java:
User user = UserKt.build_user(null, "Hello", null, "Porterhouse Steak");
System.out.println(user);
Output from the example:
User(userNumber=-1, name=Hello, userGroups=[], screenName=Porterhouse Steak)
The method is somewhere between a constructor and a builder. It beats hammering out a full-blown Builder object, and avoids cluttering your data class with unnecessary Java-interop glue code messiness.
See Package Level Functions for more information.