In Kotlin, is it possible to set different enum values to different objects, similar to how Python handles enum values? - kotlin

I want to create a color class in Kotlin, that takes in an RGB value. I also plan on adding hex values but that doesn't pertain to the question. I would have two classes, then.
data class RGB(val red: Int, val green: Int, val blue: Int)
The RGB Class just stores three ints.
class Color(rgb: RGB)
The Color class just stores an RGB value. (I'm doing it this way because I plan to add a secondary constructor to Color that excepts a hex object instead.)
I want to have an enumeration that stores several generic colors, but I want each value in the enum to actually be a color object, not just store one. My current Colors enum is as follows.
enum class Colors(color: Color) {
WHITE(Color(RGB(255,255,255))),
//Some other colors
BLACK(Color(RGB(0,0,0)));
}
The issue I have with this is that I can't pass these values into a color parameter or set a color object equal to them (because they aren't colors, and just store them).
In Python, for enumerations, you can actually set enum values as objects, and therefore can be used in place of any object of that type. I don't remember the syntax exactly, but it was something along the lines of
class Colors(enum) {
WHITE = Color(RGB(255,255,255)),
//Some other colors
BLACK = Color(RGB(0,0,0));
}
This way if I have some method with a parameter of type color
//We've moved back into Kotlin
fun someMethod(color: Color) {
//Color some stuff
}
I can actually pass in an object from the enum.
someMethod(Colors.WHITE)
I am aware that using my current Kotlin enum, I can just call Colors.WHITE.color but I just want to know if I can do it the Python way. My Colors enum is just an example, but I have other uses where it is more important.
TL;DR I want to be able to use polymorphism on individual enum values, by setting them equal to objects. I want to be able to pass them into parameters of type X by setting them equal to X(parameters)

I often see that people use an Enum as a way of defining some constants - they do not necessarily want to take advantage of some ordinal value, or the closed set of members.
If you want to take advantage of a closed set of members property of an Enum type thing, then a sealed class could be your solution. A sealed class has a fixed set of implementations, so you can have things like switch/when statements that know they have exhuasted all cases in the set.
As an aside, this property is what makes sealed class the perfect tool for implementing aglebraic data types in a way that keeps your handling of a value that could be one of several completely different things safe. Eunms are all the same thing and have the same functions, a sealed class could consist of objects and instances that have nothing in common.
Here is an example.
import java.awt.Color
sealed class MyColor(r: Int, g: Int, b: Int) : Color(r, g, b) {
object WHITE : MyColor(255, 255, 255)
object BLACK : MyColor(0, 0, 0)
}
fun doSomethingWithAColor(color: Color) {
//Your Stuff
}
fun main() {
val color: MyColor = MyColor.WHITE
// This when clause is able to know that it has handled every case or not
when (color) {
MyColor.WHITE -> doSomethingWithAColor(color)
MyColor.BLACK -> doSomethingWithAColor(color)
}
}
If all you actually want is a set of constants, the sealed class solution provides that, but a normal old static class with immutable members and a private constructor will do. Here is an example, and it will work the same way as the example above.
class MyColor private constructor(r: Int, g: Int, b: Int): Color(r,g,b){
companion object{
val WHITE = MyColor(255, 255, 255)
val BLACK = MyColor(0, 0, 0)
}
}
If you do want to take advantage of an Enum's ordinal value (but from your python example I don't think you do) then I am afraid I am out of solutions that come out of the box. I think you would need to just provide more custom functions on your top level class to mimic that sort of number to instance mapping and have the type inheritance you are looking for.

Related

Allow reference to variables in class declared with Any

I have a function what take a List It can be many difference classes that contain a number of fields (open, high, low, close). The classes contain other fields. But I don't want to write one function for each class that I am going to map over. Is there a way to allow for the fields to be specified without getting "unresolved reference"
fun createTA4JBarsFromAlgobars(timebars: List<Any?>, name: String): BaseBarSeries? {
val series = BaseBarSeriesBuilder().withName(name).build()
//all it.unixSecond etc. Get unresolved reference with compiling
timebars.map { series.addBar(utilService.unixtimeToZoneDateTime(it.unixSecond), it.open, it.high, it.low, it.close, it.volume) }
return series
}
It is extremely rare to ever have a reason to have a List<Any>. (In 12 years of Java and Kotlin use, I don't recall ever doing that.) If there is some property you want to get from all the items in the list, you should have all of them inherit from a common interface that defines those properties. Then you can create a list of that interface type and put in instances of various different classes that all conform to that interface. For example:
interface TimeBar {
val unixSecond: Long
val open: Float
val close: Float
val high: Float
val low: Float
val volume: Float
}
// Make all your various classes for your list implementers of the above interface.
// Then make your list a `List<TimeBar>`.
In your case, if each class type doesn't have high, low, etc. properties, but instead they all get added to your chart in various different ways, you could instead create a common interface for adding the instance to your chart, for example:
interface TimeBar {
fun addToBaseBarSeries(series: BaseBarSeries)
}
// in each of your classes, some implementation of the above function:
override fun addToBaseBarSeries(series: BaseBarSeries) {
series.addBar(utilService.unixtimeToZoneDateTime(unixSecond), open, high, low, close, volume)
}
You are also using map improperly. It is not for creating side effects. It is for creating a new list of a different type. You should be using a for loop instead.

Generic variance type parameter(Kotlin)

I do not fully understand how variance in Generics work. In the code below the classes are as follows Any -> Mammals -> Cats. Any is the supertype, there is a parameter called from in the copy function
From what I understand about the out and in keywords, out allows reference to any of it's subtype, can only be produced not consumed.
in allows reference to any of it's supertype, can only be consumed not produced.
However in the copytest function we are instantiating the function copy. I gave it a catlist1 argument in the from parameter. Since the parameter has an out keyword wouldn't it mean that we can only input parameters that are a subtype of catlist2?
To top of my confusion I have seen many conflicting definitions, for instance , In Kotlin, we can use the out keyword on the generic type which means we can assign this reference to any of its supertypes.
Now I am really confused could anybody guide me on how all of these works? Preferably from scratch, thanks!
class list2<ITEM>{
val data = mutableListOf<ITEM>()
fun get(n:Int):ITEM = data[n]
fun add(Item:ITEM){data.add(Item)}
}
fun <T> Copy(from: list2<out T>, to:list2<T>){
}
fun copytest(){
val catlist1 = list2<Cat>()
val catlist2 = list2<Cat>()
val mammallist = list2<Mammal>()
Copy(catlist1,mammallist)
}
I think maybe you're mixing up class-declaration-site generics and use-site generics.
Class-declaration-site generics
Defined at the class declaration site with covariant out, it is true you cannot use the generic type as the type of a function parameter for any functions in the class.
class MyList<out T>(
private val items: Array<T>
) {
fun pullRandomItem(): T { // allowed
return items.random()
}
fun addItem(item: T) { // Not allowed by compiler!
// ...
}
}
// Reason:
val cowList = MyList<Cow>(arrayOf(Cow()))
// The declaration site out covariance allows us to up-cast to a more general type.
// It makes logical sense, any cow you pull out of the original list qualifies as an animal.
val animalList: MyList<Animal> = cowList
// If it let us put an item in, though:
animalList.addItem(Horse())
// Now there's a horse in the cow list. That doesn't make logical sense
cowList.pullRandomItem() // Might return a Horse, impossible!
It is not logical to say, "I'm going to put a horse in a list that may have the requirement that all items retrieved from it must be cows."
Use-site generics
This has nothing to do with the class level restriction. It's only describing what kind of input the function gets. It is perfectly logical to say, "my function does something with a container that I'm going to pull something out of".
// Given a class with no declaration-site covariance of contravariance:
class Bag<T: Any>(var contents: T?)
// This function will take any bag of food as a parameter. Inside the function, it will
// only get things out of the bag. It won't put things in it. This makes it possible
// to pass a Bag of Chips or a Bag of Pretzels
fun eatBagContents(bagOfAnything: Bag<out Food>) {
eat(bagOfAnything.contents) // we know the contents are food so this is OK
bagOfAnything.contents = myChips // Not allowed! we don't know what kind of stuff
// this bag is permitted to contain
}
// If we didn't define the function with "out"
fun eatBagContentsAndPutInSomething(bagOfAnything: Bag<Food>) {
eat(bagOfAnything.contents) // this is fine, we know it's food
bagOfAnything.contents = myChips // this is fine, the bag can hold any kind of Food
}
// but now you cannot do this
val myBagOfPretzels: Bag<Pretzels> = Bag(somePretzels)
eatBagContentsAndPutInSomething(myBagOfPretzels) // Not allowed! This function would
// try to put chips in this pretzels-only bag.
Combining both
What could be confusing to you is if you saw an example that combines both of the above. You can have a class where T is a declaration site type, but the class has functions where there are input parameters where T is part of the definition of what parameters the function can take. For example:
abstract class ComplicatedCopier<T> {
abstract fun createCopy(item: T): T
fun createCopiesFromBagToAnother(copyFrom: Bag<out T>, copyTo: Bag<in T>) {
val originalItem = copyFrom.contents
val copiedItem = createCopy(originalItem)
copyTo.contents = copiedItem
}
}
This logically makes sense since the class generic type has no variance restriction at the declaration site. This function has one bag that it's allowed to take items out of, and one bag that it's allowed to put items into. These in and out keywords make it more permissive of what types of bags you can pass to it, but it limits what you're allowed to do with each of those bags inside the function.

Creating the analogue of BigInteger.ZERO for own data type in Kotlin

So, as is well-known, Kotlin is able to access Java libraries, and among them is the BigInteger class. And this class has a very handy feature: There is a keyword, called "BigInteger.ZERO", which returns a BigInteger object whose value equals zero.
Now I am writing a fraction data type, and I'd very much like to do the same thing for it. But the problem with just putting a val inside the class is that this first needs an object to begin with; it's not a "static" constant, so to say.
I'd be very grateful indeed for any forthcoming replies.
You can put the constant as a val inside the companion object of your class:
class Fraction {
...
companion object {
val ZERO = Fraction()
}
}
Then you can call your constant by Fraction.ZERO.
Note that this only makes sense if your fraction class is immutable.

How to modify the parameter pass to kotlin data class?

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.

Way to share data between classes in kotlin

I have 2 kotlin classes and both of them implement the interface myVariables. Inside myVariables is a variable named money. What I am trying to do is have the same variable(and keep its value too) be used inside both classes. Is this a good way to do it?
class MainActivity : myVariables, AppCompatActivity() {override val money = 0}
abstract class ShopActivity : myVariables, AppCompatActivity() {override val money = 0}
The interface:
interface myVariables {
val money: Int
}
What is a better way to use the same variable in both of my classes without redefining its value. For example if the variable has gained a value of 5 in the MainActivity class, I want to use the same variable with a value of 5 in the ShopActivity class.
I want the same effect as if this variable was global in the file that is using it, if that makes any sense.
You're looking for singleton. And the idiomatic way to create them in Kotlin is to create object (not class), which will hold your variables. Like this:
object MyVariables {
const val string = "foo"
val pops = 4
}
And then you can use it in your class like this:
class MyClass {
fun myMethod() {
println(MyVariables.string)
}
}
Of course you can use vars, not vals if you need to change them. But be warned that having a global mutable state is generally a bad idea because it's hard to track over the code, where variable is changed from.
Also note that generally it's a bad idea to start names of interfaces from lowercase because it breaks conventions and makes code less readable. It took a couple seconds for me to understand that myVariables isn't variable name.