Kotlin companion object unexpected result - kotlin

sealed class Color () {
sealed class Dark (): Color() {
object DarkRed : Dark()
object DarkBlue : Dark()
}
override fun toString(): String = when (this) {
is Color.Dark -> "Dark"
is Color.Dark.DarkRed -> "Dark Red"
is Color.Dark.DarkBlue -> "Dark Blue"
}
companion object Companion {
fun make(name: String): Color = when (name) {
"DarkRed" -> Color.Dark.DarkRed
"DarkBlue" -> Color.Dark.DarkBlue
else -> throw Exception ("Error unkown color '$name'")
}
}
}
fun main(args: Array<String>) {
val color = Color.Companion.make("DarkRed")
println (color.toString()) // Dark is printed not "Dark Red"
}
The code above prints Dark while I expected Dark Red. It seems that the make returned type Color.Dark.DarkRed is interpreted as Color.Dark by the ofString() function, why ?

Because 'DarkRed' is both Dark and DarkRed, and is Dark is checked before is DarkRed.
when will enter the first clause that resolves to true.
To fix this put the most specific checks before the lesser specific checks.

You can just put line is Color.Dark -> "Dark" in the end of toString function. In your case is Color.Dark returns true for DarkRed.

Related

ReadOnlyProperty - type arguments automatically inferred not validating bounds

I'm trying to inject value into class property using by keyword.
No problem implementing the code below.
open class Component
class ComponentA: Component()
class Equipment(val components: List<Component>) {
inline fun <reified T> getComponent(): T? {
return components.firstOrNull { it is T } as T?
}
val ca: ComponentA by find<ComponentA>() //this is what I intend to do
val ca1: List<ComponentA> by find() //no error or warning
val ca2 by find<List<ComponentA>>() //Error: Type argument is not within its bounds
}
inline fun <reified T> find(): ReadOnlyProperty<Equipment, T> where T : Component {
return ReadOnlyProperty { ref, _ -> ref.getComponent<T>() ?: throw Exception("Not found") }
}
But I find it is weird that the val ca1: List<ComponentA> by find() not showing any error or warning. Error still show normally when I try val ca2 by find<List<ComponentA>>().
Can anybody explain why this happened and how to make sure ca1 definition syntax is invalid.
I'm using:
AdoptOpenJDK 11.0.7.10 hotspot
Kotlin 1.6.21
IntelliJ IDEA 2021.3.2
well I think the problem is from IDEA with the compiler, I tried using android functions like viewModels() with List instead of ViewModel and I had the same problem, the IDEA will not show any error:
but when you compile the code you will get an error:
so you can ignore this, it's just a problem with the IDEA, also you have some mistakes in your code,
val components: List<Equipment> this should be val components: List< Component >
return components.firstOrNull { it is T } as T and this should be return components.firstOrNull { it is T } as T? because you cannot cast a null type to a non-null type
so your code should look like this:
open class Component
class ComponentA: Component()
class Equipment(val components: List<Component>) {
inline fun <reified T> getComponent(): T? {
return components.firstOrNull { it is T } as T?
}
val ca: ComponentA by find<ComponentA>() //this is what I intend to do
// val ca1: List<ComponentA> by find() //no error or warning
// val ca2 by find<List<ComponentA>>() //Error: Type argument is not within its bounds
}
inline fun <reified T> find(): ReadOnlyProperty<Equipment, T> where T : Component {
return ReadOnlyProperty { ref, _ -> ref.getComponent<T>() ?: throw Exception("Not found") }
}

Implement Iterable in an immutable LinkedList in Kotlin

I'm trying to understand the functional programming paradigm so I'm playing around with an immutable linked list. I've created a Bag with some utility functions and now I want to iterate through the collection. I want to implement an Iterable:
sealed class Bag<out A> : Iterable<A> {
companion object {
fun <A> of(vararg aa: A): Bag<A> {
val tail = aa.sliceArray(1 until aa.size)
return if (aa.isEmpty()) Nil else Cons(aa[0], of(*tail))
}
/**
* Returns the tail of the bag
*/
fun <A> tail(bag: Bag<A>): Bag<A> =
when (bag) {
is Cons -> bag.tail
is Nil -> throw IllegalArgumentException("Nil cannot have a tail")
}
/**
* Add an item to the beginning
*/
fun <A> add(bag: Bag<A>, elem: A): Bag<A> =
Cons(elem, bag)
fun <A> isEmpty(bag: Bag<A>): Boolean =
when (bag) {
is Nil -> true
is Cons -> false
}
}
class BagIterator<A> : Iterator<A> {
override fun hasNext(): Boolean {
TODO("Not yet implemented")
}
override fun next(): A {
TODO("Not yet implemented")
}
}
}
object Nil : Bag<Nothing>() {
override fun iterator(): Iterator<Nothing> =
BagIterator()
}
data class Cons<out A>(val head: A, val tail: Bag<A>) : Bag<A>() {
override fun iterator(): Iterator<A> =
BagIterator()
}
Now I'm stuck with hasNext() and next() implementations. I'm not even sure if this approach works. Can I implement Iterable this way?
Note that an Iterator is a mutable thing. next must mutate the iterator's current state. Its signature does not allow you to "return a new Iterator with a different state". So if you wanted to do that, sad news for you :( This is because the way that iteration is supposed to happen is (this is roughly what a for loop translates to):
val iterator = something.iterator()
while (iterator.hasNext()) {
val elem = iterator.next()
...
}
Now knowing that, we can store a var current: Bag<A>:
// in Bag<A>
class BagIterator<A>(var current: Bag<A>) : Iterator<A> {
override fun hasNext(): Boolean = current !is Nil
override fun next(): A {
val curr = current
return when (curr) {
is Nil -> throw NoSuchElementException()
is Cons -> curr.also {
current = it.tail
}.head
}
}
}
override fun iterator(): Iterator<A> = BagIterator(this)
And the Nil and Cons types can have empty bodies.
If you don't like this, blame the standard library designers :) You can always write your own Iterator<A> interface, but of course you can't use the for loop with your Bag if you do that. You can write your own forEach extension function though.

why lambda function parameter's type is Nothing on generic type with asterisk in kotlin?

when i call some api, i wished use multiple callback with generic parameter.
so i defined CallBackData class
class CallBackData<T>(val func: (T?) -> Boolean, val params: T?)
it not data class. because it super class of other callbacks.
and i define Array<CallBackData<*>> variable for multiple callback.
val callbackDts : Array<CallBackData<*>> = arrayOf(
CallBackData(::sampleCallback1, SomeClass(1)),
CallBackData(::sampleCallback2, "hello"),
CallBackData(::sampleCallback3, -1),
)
but when i call func, it say error
Type mismatch.
Required: Nothing?
Found: Any?
i don't get it. why? isn't same it.params type T is same of it.func(param(T))? right? why is Nothing Type? why is not same?
this is full code
fun start(){
val callbackDts : Array<CallBackData<*>> = arrayOf(
CallBackData(::sampleCallback1, SomeClass(1)),
CallBackData(::sampleCallback2, "hello"),
CallBackData(::sampleCallback3, -1),
)
callApi(callbackDts)
}
fun callApi(callbacks : Array<CallBackData<*>>){
callbacks.forEach{
it.func(it.params)
}
}
fun sampleCallback1(params: SomeClass?) : Boolean {
println("sampleCallback1 ${params.toString()}")
return true
}
fun sampleCallback2(params: String?) : Boolean {
println("sampleCallback2 $params")
return true
}
fun sampleCallback3(params: Int?) : Boolean {
println("sampleCallback3 $params")
return true
}
data class SomeClass(val i:Int)
class CallBackData<T>(val func : (T?) -> Boolean, val params: T?)
i tried convert to like this (using out keyword), but it's failed same.(Lambda's parameter type is Nothing?)
fun start(){
val callbackDts : Array<CallBackData<out Any?>> = arrayOf(
CallBackData(::sampleCallback1, SomeClass(1)),
CallBackData(::sampleCallback2, "hello"),
CallBackData(::sampleCallback3, -1),
)
callApi(callbackDts)
}
fun callApi(callbacks : Array<CallBackData<out Any?>>){
callbacks.forEach{
it.func(it.params)
}
}
fun sampleCallback1(params: SomeClass?) : Boolean {
println("sampleCallback1 ${params.toString()}")
return true
}
fun sampleCallback2(params: String?) : Boolean {
println("sampleCallback2 $params")
return true
}
fun sampleCallback3(params: Int?) : Boolean {
println("sampleCallback3 $params")
return true
}
data class SomeClass(val i:Int)
class CallBackData<T>(val func : (T?) -> Boolean, val params: T?)
i look forward to your reply. thanks!
Unfortunately, the type information of T is gone once you projected a CallbackData<T> to CallbackData<*>. It is no longer known that it.func takes the same type as it.params.
But you do know that they are the same type in the CallBackData class itself, don't you? So you can just add a call method
class CallBackData<T>(val func : (T?) -> Boolean, var params: T?) {
fun call() = func(params)
}
and
callbacks.forEach{
it.call()
}
Or you can overload the invoke operator:
operator fun invoke() = func(params)
You would then be able to do it() directly.
Even if you don't have control over CallBackData, you can still add an extension function:
operator fun <T> CallBackData<T>.invoke() = func(params)
Adding to other answers: if this is the only reason why you defined the CallBackData, then you don't really need this class. Kotlin has support for closures, so we don't need to intercept functions and parameters separately:
fun start(){
val callbackDts = arrayOf<() -> Unit>(
{ sampleCallback1(SomeClass(1)) },
{ sampleCallback2("hello") },
{ sampleCallback3(-1) },
)
callApi(callbackDts)
}
fun callApi(callbacks : Array<() -> Unit>){
callbacks.forEach{
it()
}
}
You can define a function
fun <T> CallBackData<T>.call() = func(params)
and then callApi can be changed to:
fun callApi(callbacks : Array<CallBackData<*>>){
callbacks.forEach{ it.call() }
}
Then Kotlin does not have a problem to infer that the types of func and params match for each CallBackData.

While using Kotlin, I'm having trouble using mutableSetOf() function. The function doesn't exist. The IDE gives a "Create function" option

//Get volgate: Returns new voltage
fun getVoltage() : MutableSet<T> {
//Error exists: mutableSetOf() function.
val mFinalValue : MutableSet<T> = mutableSetOf()
.....
....
}
I have created this:
class X<T> {
fun some(): MutableSet<T> {
val x: MutableSet<T> = mutableSetOf()
return x
}
}
And I have no warning. So my suggestion to you is to invalidate/restart your Android Studio Cache, and sync gradle/rebuild your project.
With the information you provided, there's nothing inherently wrong at first sight.
You should specify generic parameter T in the function signature
fun <T> getVoltage() : MutableSet<T>
instead of
fun getVoltage() : MutableSet<T>

Create a function in Kotlin that takes an enum parameter

I'm creating a function that takes an enum value as a parameter, but I am very new to Kotlin and I can't find any material that covers this specifically.
Example:
enum class Color(val rgb: Int) {
RED(0xFF0000),
ORANGE(0xffa500),
YELLOW(0xffff00),
GREEN(0x00FF00),
BLUE(0x0000FF),
INDIGO(0x4b0082),
VIOLET(0x8F5E99)
}
fun getHexColor (Color: Enum)
{
when(x){
Color.BLUE -> println("Battle")
else -> print("otherwise")
}
}
I get an error that says:
One type argument expected for class Enum<E: Enum<E>>
I've looked through Kotlin documentation for over an hour and I've got nothing to show for it... do any of you have an idea of how to use this class as a parameter?
enum creates a new class so you can use it as function argument type, as shown below.
For functions in kotlin see here.
fun getHexColor (x : Color)
{
when(x){
Color.BLUE -> println("Battle")
else -> print("otherwise")
}
}
You have to use the type which is Color:
fun getHexColor (x: Color) {
when(x){
Color.BLUE -> println("Battle")
else -> print("otherwise")
}
}
Note that a function prefixed with "get" should return something. Since when is an expression you can do it like this:
fun getHexColor (x: Color) = when(x) { // will return a String
Color.BLUE -> "Battle"
else -> "otherwise"
}
println(getHexColor(Color.BLUE))
Enum is actually special kind of class (it is even called enum class). So, use it as normal class and use benefits you get from it.
Example:
enum class X {
X, Y
}
fun check(param: X) {
val unit = when (param) {
X.X -> println("x")
X.Y -> println('y')
}
}
A function syntax in Kotlin looks like this:
fun double(x: Int): Int {
return 2 * x
}
where x is the name of the function parameter of type Int. Your function is not valid since you use Color as the parameter name rather than its type. To fix it do:
fun getHexColor (color: Color) {
when(color){
Color.BLUE -> println("Battle")
else -> print("otherwise")
}
}
You are able to do it like this with an interface for example:
enum class Color(val rgb: Int): IHexColor {
RED(0xFF0000){
override fun getHexColor() = rgb.toString()
},
GREEN(0x00FF00){
override fun getHexColor(): String = rgb.toString()
},
BLUE(0x0000FF){
override fun getHexColor(): String = rgb.toString()
}
}
interface IHexColor {
fun getHexColor(): String
}
#Test
fun testBasic() {
val red = Color.RED
val green = Color.GREEN
val blue = Color.BLUE
val palette = arrayListOf(red, green, blue)
palette.forEach {
println("Color: $it :: hex - ${it.getHexColor()}")
}
}
// output => Color: RED :: hex - 16711680, Color: GREEN :: hex - 65280, Color: BLUE :: hex - 255
How to use enum class:
fun useColorClass(color: Color){
println(color.rgb)
}
#Test
fun testColor() {
useColorClass(Color.RED)
useColorClass(Color.BLUE)
useColorClass(Color.GREEN)
}
// output => 16711680, 255, 65280
An answer for your question:
fun getHexColor (c: Color): String {
return when(x){
Color.BLUE -> println("Battle")
else -> print("otherwise")
}
}