Is it possible to create such a map that is granteed to contain all enum keys as its keys?
Here is what I'm trying to achieve but with null-safety garanteed.
enum class Product { A, B }
private val _products = mapOf<Product, MutableLiveData<Int>>(
Product.A to MutableLiveData(),
Product.B to MutableLiveData()
)
val products = mapOf<Product, LiveData<Int>>(
Product.A to _products[Product.A]!!,
Product.B to _products[Product.B]!!
)
Update
The maps construction can be simplified like this.
Thanks to #Michael for tha second line.
private val _products = Product.values().associateWith { MutableLiveData<Int>() }
val products: Map<Product, LiveData<Int> = _products
This solves the issue of construction a map with all enum keys, but yet does not let one get a value without non-null assition:
val a: Int = products[Product.A]!!.value
Related
In python you can get a unique numeric ID for any object via id(object):
person = Person()
person_id = id(person)
What is the equivalent function in Kotlin?
Classes in Kotlin do not automatically have a unique ID. As mentioned in the comments, you can get the identityHashCode. It is not guaranteed unique, but in practice if you are just using it to compare items in a log, it is probably sufficient.
class Person() {
val id: Int get() = System.identityHashCode(this)
}
If you need unique IDs, you could assign them at construction time using a counter in a companion object.
class Person() {
val id: Long = nextId
companion object {
private var nextId: Long = 0L
get() = synchronized(this) { ++field }
set(_) = error("unsupported")
}
}
// Or simpler on JVM:
class Person() {
val id: Long = idCounter.getAndIncrement()
companion object {
private val idCounter = AtomicLong(1L)
}
}
Or if you are on JVM, you can use the UUID class to generate a statistically unique ID for each class as it is instantiated, but this is probably not very useful just for logging.
class Person() {
val id = UUID.randomUUID()
}
I have a scenario whereby I need a map containing duplicate keys and values. I have created a list first and then I used associatedBy to convert them to a map, however the duplicates issue is not taken into account. Here is my implementation:
class State(private val startStatus: Status, private val expectedStatus: Status) {
companion object StatusList {
val listStatuses = listOf(
State(Status.A, Status.B),
State(Status.B, Status.A),
State(Status.B, Status.C),
State(Status.C, Status.B),
State(Status.C, Status.E),
State(Status.C, Status.D),
State(Status.D, Status.B),
State(Status.E, Status.C),
State(Status.E, Status.B)
)
open fun mapStatuses(): Map<Status, Collection<Status>> {
return listStatuses.associateBy(
keySelector = { key -> key.expectedStatus },
valueTransform = State::startStatus)
}
}
}
I am struggling to find a Multimap in Kotlin that would allow me to deal with duplicates. Can you help?
Thanks
In short, there is no multimap in Kotlin.
A multimap would allow multiple, equivalent keys with different values - this can be implemented with unique keys and a collection of values associated with given key, instead of having a view over collection of key-value pairs with equivalent keys.
Thus, you can use groupBy():
data class Record(val id: Int, val name: String)
fun main() {
val records = listOf(
Record(1, "hello"),
Record(1, "there"),
Record(2, "general"),
Record(2, "kenobi")
)
val mapped = records.groupBy({ it.id }, { it.name })
for (entry in mapped) {
println("${entry.key} -> ${entry.value.joinToString()}")
}
}
Here I am using groupBy with a projection of key (which is Record's id) and a projection of value (which is Record's name). Quite similar to your States and Statuses.
I would like to initialize enum by its associated value.
My enum:
enum class DirectionSwiped(raw: Int){
LEFT(4),
RIGHT(8);
}
I would like to initialize it as such:
val direction = DirectionSwiped(raw: 4)
But I get this error:
Enum type cannot be instantiated
Why is this happening? In Swift, this functionality works like this:
enum Direction: Int {
case right = 2
}
let direction = Direction(rawValue: 2)
How can I make it work in Kotlin?
Yes you can
enum class DirectionSwiped(val raw: Int){
LEFT(4),
RIGHT(8);
}
val left = DirectionSwiped.LEFT
val right = DirectionSwiped.RIGHT
val leftRaw = DirectionSwiped.LEFT.raw
val rightRaw = DirectionSwiped.LEFT.raw
val fromRaw = DirectionSwiped.values().firstOrNull { it.raw == 5 }
This would be the correct way to access the instances of the enum class
What you are trying to do is create a new instance outside the definition site, which is not possible for enum or sealed classes, that's why the error says the constructor is private
As the error says, you cannot instantiate enums in Kotlin. A possible workaround would be to use a map and 2 helper methods to get enum values from raw values and vice versa:
enum class DirectionSwiped {
LEFT,
RIGHT;
fun toRaw() = enumToRaw[this]
companion object {
val rawToEnum = mapOf(
4 to LEFT,
8 to RIGHT
)
val enumToRaw = rawToEnum.entries.associate{(k,v)-> v to k}
fun ofRaw(raw: Int): DirectionSwiped? = rawToEnum[raw]
}
}
Usage:
val direction = DirectionSwiped.ofRaw(4) // LEFT
val raw = DirectionSwiped.LEFT.toRaw() // 4
Like the title says:
class Answers {
enum class Answer(text: String) {
YES("Yes."),
No("No."),
MAYBE("Maybe."),
AGAIN("Ask again.")
}
val answers = Answer.values()
val rand = Random()
fun genAnswer ():String {
val n = rand.nextInt(3)+1
// return Answer[answers[n]].text
}
}
I want to pick an enum element randomly and return its text property. However, it seems I can't use its value to retrieve the element.
You can get a random enum value by doing:
val randomAnswer = Answer.values().toList().shuffled().first().text
Keep in mind that it goes for convenience over performance.
Remember to expose the text property with val. For now, it's just a constructor param:
enum class Answer(val text: String)
Answer.values().random() will do the job for you in Kotlin.
When you write val/var within the constructor for a param, it declares a property inside the class. When you do not write it, it is simply a parameter passed to the primary constructor. So just add val to the constructor param text which will make it a property with a getter and above program will work..
import java.util.*
class Answers {
enum class Answer(val text: String) {
YES("Yes."),
No("No."),
MAYBE("Maybe."),
AGAIN("Ask again.")
}
val answers: Array<Answer> = Answer.values()
val rand = Random()
fun genAnswer ():String {
val n = rand.nextInt(3)+1
return answers[n].text
}
}
fun main(String: Array<String>) {
print(Answers().genAnswer())
}
I use this generic functions for any type of enum
/**
* return random enum value of enum class T
*/
inline fun <reified T : Enum<T>> randomEnum(): T {
val enumValues: Array<T> = enumValues()
return enumValues[(0 until enumValues.size).random()]
}
/**
* return random Int from start to end of range
*/
fun IntRange.random(): Int =
Random().nextInt((endInclusive + 1) - start) + start
I had a Enum SupportedLocale with values for languages
I did it in 2 steps
Get a list of values for enum:
val languages: Array<SupportedLocale> = SupportedLocale.values()
Now select random value using ordinate position
val randomLocale = languages[Random().nextInt(languages.size)]
An easy solution that I have used is
enum class OPERATOR(val symbol:String){MULTIPLY("*"),DIVIDE("/"),ADD("+"),SUBTRACT("-"),REMINDER("-")}
private fun getRandomOperator():String{
val limit=OPERATOR.values().size-1
return OPERATOR.values()[IntRange(0,limit).random()].symbol
}
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.