I want to overload function with the same parameters (or without parameters at all) and different return types. Correct implementation should be chosen by the type of variable I assign returning value of a function to.
The only way to do this I found is using reified generics and comparing KClass'es:
inline fun <reified T: Any> read(): T {
return read(T::class)
}
#Suppress("UNCHECKED_CAST")
fun <T: Any> read(t: KClass<T>): T {
return when (t) {
Int::class -> readInt() as T
UInt::class -> readUInt() as T
String::class -> readString() as T
// ...
else -> throw Exception("Unsupported type")
}
}
fun readInt(): Int {
// ...
}
fun readUInt(): UInt {
// ...
}
fun readString(): String {
// ...
}
The problem with this approach is that the compiler and IDEA are not smart enough to determine types at compile time for which there is no implementation. The most I can do is throw a runtime exception:
val int: Int = read()
val string: String = read()
val double: Double = read()
// ^^^^ No compile-time error here
Maybe I'm missing something and there is more "correct" way of doing this?
Maybe I'm missing something and there is more "correct" way of doing this?
No. You cannot do this at all. You must name the methods differently.
I have a simple task in Kotlin of listing enum values:
interface DisplayableEnum<E: Enum<E>> {
val displayValue: String
}
inline fun <reified T> printAllValues(selected: T?) where T: Enum<T>, T: DisplayableEnum<T> {
println("All Values:")
enumValues<T>().forEach {
println(it.displayValue)
}
selected?.let { println("\nSelected: ${it.displayValue}") }
}
////// USAGE
enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B")
}
fun main() {
// with a selected value
f(MyEnum.A)
// without a selected value
f(null as MyEnum?)
}
Now imagine that all the enums I pass to printAllValues should also have a field called defaultValue. In case of MyEnum I would write it the following way:
enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B");
companion object {
val defaultValue = A
}
}
So my question: is there a way to define such a contract in Kotlin?
Ideally, I would like to somehow define that contract in an interface, like DisplayableEnum above, and then somehow use it in printAllValues like this:
inline fun <reified T> printAllValues(selected: T) where T: Enum<T>, T: DisplayableEnum<T> {
println("All Values:")
enumValues<T>().forEach {
println(it.displayValue)
}
selected?.let { println("\nSelected: ${it.displayValue}") }
println("Default value: ${T.defaultValue???}"
}
The one thing that I don't want is using non-companion defaultValue, I always have to either pass it to the function manually (but why if the type contains all the info?) or, if made non-companion:
interface DisplayableEnum<E: Enum<E>> {
val displayValue: String
val defaultValue: E
}
then access it through an object - do something ugly like enumValues<T>().first().defaultValue.
I wonder if Kotlin has a solution in this case.
It's impossible to define abstract properties in companion object.
So it should be defined in interface directly.
The tricky part here is implementing of this interface without compiler warnings:
enum class MyEnum(override val displayValue: String) : DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B");
override val defaultValue: MyEnum by lazy { A }
}
If enumValues<T>().first().defaultValue looks ugly to you, wrap it into auxilary function:
inline fun <reified T> enumDefaultValue(): T where T : Enum<T>, T : DisplayableEnum<T> =
enumValues<T>().first().defaultValue
//Usage:
println("Default value: ${enumDefaultValue<T>()}")
Let's say I have a simple class Foo with a nullable String?
data class Foo(
val bar: String?
)
and I create a simple function capitalize
fun captitalize(foo: Foo) = when {
foo.bar != null -> runCatching { foo.bar.capitalize() }
else -> ""
}
which works fine, because the compiler infers that foo.bar cannot be null eventhough it's type is nullable. But then I decide to write the same function as an extension of Foo
fun Foo.captitalize2() = when {
bar != null -> runCatching { bar.capitalize() }
else -> ""
}
and all of a sudden the compiler is no longer able to infer that bar is not null, and IntelliJ tells me that "only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable reciever of type String?"
Can anyone explain why?
I think it's because in the first case you are calling this function:
public inline fun <R> runCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
but in the second case you are calling function with receiver:
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
For me, it looks like an issue in the Kotlin compiler because if you inline code of this function by yourself it will work fine:
fun Foo.captitalize2() = when {
bar != null -> try {
Result.success(bar.capitalize())
} catch (e: Throwable) {
Result.failure<String>(e)
}
else -> ""
}
btw, if I were you I would like to write my capitalize2 function like this :)
fun Foo.captitalize2() = bar?.capitalize() ?: ""
So, finally I found an alternative approach that allows us to use runCatching without having the problem you shows.
As in my comment to the answer of #Andrei Tanana, in your code type parameters of fun <T, R> T.runCatching(block: () -> R) : Result<R> are inferred as <Foo, String> and the compiler can't use the information that this.bar is not null.
If you rewrite the capitalize2 function as follows
fun Foo.capitalize2(): Serializable = when {
bar != null -> bar.runCatching { capitalize() }
else -> ""
}
T is inferred as String (thanks of the bar != null case of the when expression) and the compiler does not complain about this.capitalize() invocation in the block passed to runCatching.
I hope this can help you, both as an approach than allows you to solve the problem and as explanation of the problem itself.
I have this enum:
enum class Types(val value: Int) {
FOO(1)
BAR(2)
FOO_BAR(3)
}
How do I create an instance of that enum using an Int?
I tried doing something like this:
val type = Types.valueOf(1)
And I get the error:
Integer literal does not conform to the expected type String
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun fromInt(value: Int) = Types.values().first { it.value == value }
}
}
You may want to add a safety check for the range and return null.
Enum#valueOf is based on name. Which means in order to use that, you'd need to use valueof("FOO"). The valueof method consequently takes a String, which explains the error. A String isn't an Int, and types matter. The reason I mentioned what it does too, is so you know this isn't the method you're looking for.
If you want to grab one based on an int value, you need to define your own function to do so. You can get the values in an enum using values(), which returns an Array<Types> in this case. You can use firstOrNull as a safe approach, or first if you prefer an exception over null.
So add a companion object (which are static relative to the enum, so you can call Types.getByValue(1234) (Types.COMPANION.getByValue(1234) from Java) over Types.FOO.getByValue(1234).
companion object {
private val VALUES = values()
fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value }
}
values() returns a new Array every time it's called, which means you should cache it locally to avoid re-creating one every single time you call getByValue. If you call values() when the method is called, you risk re-creating it repeatedly (depending on how many times you actually call it though), which is a waste of memory.
Admittedly, and as discussed in the comments, this may be an insignificant optimization, depending on your use. This means you can also do:
companion object {
fun getByValue(value: Int) = values().firstOrNull { it.value == value }
}
if that's something you'd prefer for readability or some other reason.
The function could also be expanded and check based on multiple parameters, if that's something you want to do. These types of functions aren't limited to one argument.
If you are using integer value only to maintain order, which you need to access correct value, then you don't need any extra code. You can use build in value ordinal. Ordinal represents position of value in enum declaration.
Here is an example:
enum class Types {
FOO, //Types.FOO.ordinal == 0 also position == 0
BAR, //Types.BAR.ordinal == 1 also position == 1
FOO_BAR //Types.FOO_BAR.ordinal == 2 also position == 2
}
You can access ordinal value simply calling:
Types.FOO.ordinal
To get correct value of enum you can simply call:
Types.values()[0] //Returns FOO
Types.values()[1] //Returns BAR
Types.values()[2] //Returns FOO_BAR
Types.values() returns enum values in order accordingly to declaration.
Summary:
Types.values(Types.FOO.ordinal) == Types.FOO //This is true
If integer values don't match order (int_value != enum.ordinal) or you are using different type (string, float...), than you need to iterate and compare your custom values as it was already mentioned in this thread.
It really depends on what you actually want to do.
If you need a specific hardcoded enum value, then you can directly use Types.FOO
If you are receiving the value dynamically from somewhere else in your code, you should try to use the enum type directly in order not to have to perform this kind of conversions
If you are receiving the value from a webservice, there should be something in your deserialization tool to allow this kind of conversion (like Jackson's #JsonValue)
If you want to get the enum value based on one of its properties (like the value property here), then I'm afraid you'll have to implement your own conversion method, as #Zoe pointed out.
One way to implement this custom conversion is by adding a companion object with the conversion method:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
private val types = values().associate { it.value to it }
fun findByValue(value: Int): Types? = types[value]
}
}
Companion objects in Kotlin are meant to contain members that belong to the class but that are not tied to any instance (like Java's static members).
Implementing the method there allows you to access your value by calling:
var bar = Types.findByValue(2) ?: error("No Types enum value found for 2")
Note that the returned value is nullable, to account for the possibility that no enum value corresponds to the parameter that was passed in. You can use the elvis operator ?: to handle that case with an error or a default value.
If you hate declaring for each enum type a companion object{ ... } to achieve EMotorcycleType.fromInt(...). Here's a solution for you.
EnumCaster object:
object EnumCaster {
inline fun <reified E : Enum<E>> fromInt(value: Int): E {
return enumValues<E>().first { it.toString().toInt() == value }
}
}
Enum example:
enum class EMotorcycleType(val value: Int){
Unknown(0),
Sport(1),
SportTouring(2),
Touring(3),
Naked(4),
Enduro(5),
SuperMoto(6),
Chopper(7),
CafeRacer(8),
.....
Count(9999);
override fun toString(): String = value.toString()
}
Usage example 1: Kotlin enum to jni and back
fun getType(): EMotorcycleType = EnumCaster.fromInt(nGetType())
private external fun nGetType(): Int
fun setType(type: EMotorcycleType) = nSetType(type.value)
private external fun nSetType(value: Int)
---- or ----
var type : EMotorcycleType
get() = EnumCaster.fromInt(nGetType())
set(value) = nSetType(value.value)
private external fun nGetType(): Int
private external fun nSetType(value: Int)
Usage example 2: Assign to val
val type = EnumCaster.fromInt<EMotorcycleType>(aValidTypeIntValue)
val typeTwo : EMotorcycleType = EnumCaster.fromInt(anotherValidTypeIntValue)
A naive way can be:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun valueOf(value: Int) = Types.values().find { it.value == value }
}
}
Then you can use
var bar = Types.valueOf(2)
Protocol orientated way with type-safety
interface RawRepresentable<T> {
val rawValue: T
}
inline fun <reified E, T> valueOf(value: T): E? where E : Enum<E>, E: RawRepresentable<T> {
return enumValues<E>().firstOrNull { it.rawValue == value }
}
enum class Types(override val rawValue: Int): RawRepresentable<Int> {
FOO(1),
BAR(2),
FOO_BAR(3);
}
Usage
val type = valueOf<Type>(2) // BAR(2)
You can use it on non-integer type, too.
I would build the 'reverse' map ahead of time. Probably not a big improvement, but also not much code.
enum class Test(val value: Int) {
A(1),
B(2);
companion object {
val reverseValues: Map<Int, Test> = values().associate { it.value to it }
fun valueFrom(i: Int): Test = reverseValues[i]!!
}
}
Edit: map...toMap() changed to associate per #hotkey's suggestion.
try this...
companion object{
fun FromInt(v:Int):Type{
return Type::class.java.constructors[0].newInstance(v) as Type
}
}
This is for anyone looking for getting the enum from its ordinal or index integer.
enum class MyEnum { RED, GREEN, BLUE }
MyEnum.values()[1] // GREEN
Another solution and its variations:
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>()[i]
enumFromIndex<MyEnum>(1) // GREEN
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>().getOrNull(i)
enumFromIndex<MyEnum>(3) ?: MyEnum.RED // RED
inline fun <reified T : Enum<T>> enumFromIndex(i: Int, default: T) =
enumValues<T>().getOrElse(i) { default }
enumFromIndex(2, MyEnum.RED) // BLUE
It is an adapted version of another answer. Also, thanks to Miha_x64 for this answer.
Another option...
enum class Types(val code: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
val map = values().associate { it.code to it }
// Get Type by code with check existing codes and default
fun getByCode(code: Int, typeDefault_param: Types = FOO): Types {
return map[code] ?: typeDefault_param
}
}
}
fun main() {
println("get 3: ${Types.getByCode(3)}")
println("get 10: ${Types.getByCode(10)}")
}
get 3: FOO_BAR
get 10: FOO
Why there is no contains method in enum classes (as well as in Java)? And how to implement it elegantly? Now I'm using this ugly approach:
val contains: Boolean =
try {
MyEnum.valueOf("some string")
true
} catch (e: IllegalArgumentException) {
false
}
enumContains
You can create an enumContains function similar to Hound Dog's answer but using reified type parameters instead.
You cannot create a JVM independent solution in Kotlin 1.0 but you can in Kotlin 1.1 using enumValues.
Kotlin 1.1
/**
* Returns `true` if enum T contains an entry with the specified name.
*/
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
return enumValues<T>().any { it.name == name}
}
Kotlin 1.0
/**
* Returns `true` if enum T contains an entry with the specified name.
*/
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
return T::class.java.enumConstants.any { it.name == name}
}
Usage
enumContains<MyEnum>("some string") // returns true or false
enumValueOfOrNull
If you also need the actual enum entry then you might consider creating an enumValueOfOrNull function instead.
Kotlin 1.1
/**
* Returns an enum entry with the specified name or `null` if no such entry was found.
*/
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
return enumValues<T>().find { it.name == name }
}
Kotlin 1.0
/**
* Returns an enum entry with the specified name or `null` if no such entry was found.
*/
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
return T::class.java.enumConstants.find { it.name == name }
}
Usage
enumValueOfOrNull<MyEnum>("some string")
You can just take values Array of your enum and use contains over it. For example:
Planets.values().map { it.name }.contains("EARTH")
But for this you need to have correct string value so you might uppercase it before search.
If you want to find enum by its value take a look at the Reverse Lookup for enums.
Edit:
As #JamesBassett suggested you can optimize it to stop looking once it finds a match.
Planets.values().any { it.name == "EARTH" }
You could do something like this:
fun <T : Enum<*>> KClass<T>.contains(value: String): Boolean {
return this.java.enumConstants.any { it.name == value }
}
MyEnum::class.contains("some string")
or if you are really adventurous
enum MyEnum {
MY_1, MY_2;
companion object {
fun isInEnum(someString: String) {
return try {
valueOf(someString)
true
} catch (e: Exception) {
false
}
}
}
}