Difference between enum class method in Kotlin for calling constant name - kotlin

Im new in kotlin, i was trying enum class and i found that these 3 method return the same thing.
package com.example.test1
fun main() {
println(Direction.valueOf("NORTH")) // print NORTH
println(Direction.NORTH) // print NORTH
println(Direction.NORTH.name) // print NORTH
}
enum class Direction(var direction: String, var distance: Int) {
NORTH("North", 20),
SOUTH("South", 30),
EAST("East", 10),
WEST("West", 15);
}
What is the difference uses between them?

Direction.valueOf("NORTH") returns value of enum class Direction by it's name. When you call println it implicitly calls toString method (that every Kotlin object implements implicitly). And toString default implementation for enum class is enum's name. You can override this method if you want.
Direction.NORTH it's actual enum instance. Like I wrote previously println implicitly calls toString method
Direction.NORTH.name returns name field of type String. It is special field, every enum class has, that returns it's name
For example if you change enum class like this:
enum class Direction(var direction: String, var distance: Int) {
NORTH("North", 20),
SOUTH("South", 30),
EAST("East", 10),
WEST("West", 15);
override fun toString(): String {
return this.name.lowercase()
}
}
first two prints will be "north"

Related

Kotlin: Why does override with additional optional arguments not work?

I'm trying to override the toString function of a data class with a custom toString that has optional arguments, but it is not working as expected:
data class LatLong(
val latitude: Double,
val longitude: Double
){
// Override keyword not allowed by compiler here
fun toString(decimals: Int = 5) =
"${"%.${decimals}f".format(latitude)}, ${"%.${decimals}f".format(longitude)}"
}
fun main() {
println(LatLong(-123.0, 49.0)) // prints: "LatLong(latitude=-123.0, longitude=49.0)" i.e. does not call custom toString
println(LatLong(-123.0, 49.0).toString()) // prints: "LatLong(latitude=-123.0, longitude=49.0)" i.e. does not call custom toString
println(LatLong(-123.0, 49.0).toString(decimals=5)) // prints: "-123.00000, 49.00000"
}
Question is how should I override it to get the behaviour that you'd expect (i.e. all 3 calls above should use the custom method)?.
I could obviously add
override fun toString() = toString(decimals=5)
But this means defining the default argument twice which is a recipe for future bugs. Of course I could define the default as a constant and reference from both toStringa, but it seems messy. It is surprising LatLong(...).toString() does not call the new method.
What is the "Kotlinic" way to handle this?
You don't need to declare the default value twice. Just declare it in the toString override, rather than in your own toString's parameter list:
override fun toString() = toString(decimals = 5)
// make this a required parameter
fun toString(decimals: Int) =
"${"%.${decimals}f".format(latitude)}, ${"%.${decimals}f".format(longitude)}"
Of course if you have more format options this would get a bit complicated, but you can always just wrap everything in a (data) class, and end up with a single parameter.
data class FormatOptions(
val decimals: Int = 5,
val someOtherOption: Int = 10
)
override fun toString() = toString(FormatOptions(/* ... */))
fun toString(options: FormatOptions): String = TODO()
Just by the way, the parameter list of the call toString() exactly matches the parameterless toString overload declared automatically by the data class. On the other hand, it only matches the one you declared if it considers optional parameters. So the compiler has very good reasons to prefer to resolve LatLong(...).toString() to the parameterless toString method, instead of the one you declared.

why is an Int not an Int inside an interface?

I have this function in side an interface
interface test<Int>{
fun printInt():Int {
return 2}}
The error is: the integer literal doesn't conform to the expected type Int.
If I change the return type to kotlin.Int, the error goes away.
interface test<Int>{
fun printInt(): **kotlin.Int** {
return 2}}
I don't use return, it works fine like this:
interface test<Int>{
fun printInt() = 2
}
if I get the printInt function out of the interface, the compiler doesn't complain:
fun printInt(): **Int** {
return 2}
what are these Int#1 and kotlin.Int?
This declaration:
interface test<Int>
declares a generic interface, with a type parameter called Int. This is similar to how MutableList is a generic interface with a type parameter called E:
public interface MutableList<E>
Therefore, inside the interface, the unqualified name "Int" refers to the type parameter, (similar to how inside MutableList, E refers to the type parameter) not the kotlin.Int type.

Enum class with name and ordinal constructors

Coming from java background, I'm wondering how do you handle Enums with string names and int ordinals.
I notice that property name and ordinal is built-in into enums as I tried to specify a constructor and it gave shadow warning. Here's my code.
enum class Department {
ACCOUNTING(0, "Accounting"),
SALES(1, "Sales"),
HR(2, "Human Resource")
}
From the documentation (bottom of the page):
Every enum constant has properties to obtain its name and position in the enum class declaration.
In your example that means that you can specify your enum as:
enum class Department {
ACCOUNTING,
SALES,
HR
}
Then
Department.values().forEach { println("${it.ordinal}: ${it.name}") }
will print
0: ACCOUNTING
1: SALES
2: HR
To add a display name (like "Human Resources") to your class, I think your best option is to add a property to your constructor:
enum class Department(val displayName: String) {
ACCOUNTING("Accounting"),
SALES("Sales"),
HR("Human Resources")
}
However, if you really want to stick to this short form, you could simply change the name accordingly:
enum class Department {
Accounting,
Sales,
`Human Resources`
}
Note the back ticks to allow for spaces in your display name. I would personally not do this, since Department.`Human Resources` is cumbersome to type and somewhat difficult to read (at least in my opinion).
You can override toString() for the specific enum class (HR) that you want to return a different value for. You can also override toString() for you parent enum and make it return a String with only the first letter capitalized, that way you can keep the naming convention intact.
enum class Department {
ACCOUNTING,
SALES,
HR {
override fun toString() = "Human Resource"
};
// capitalize first letter
override fun toString() =
super.toString().toLowerCase().capitalize()
}
you can then simply call toString() or pass it as string.
fun main() {
val hrString = HR.toString()
println(hrString) // Human Resource
println(HR) // Human Resource
println(ACCOUNTING) // Accounting
println(SALES) //Sales
}
Same as a class in kotlin, you can specify properties in the enum class constructor. For example
enum class Color( val r: Int, val g: Int, val b: Int){
RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0);
}
As you already know Enum class(The common base class of all enum classes) have built in name and ordinal properties. these properties are defined as follows
/** Returns the name of this enum constant, exactly as declared in its enum declaration. */
public final val name: String
/** Returns the ordinal of this enumeration constant (its position in its enum declaration,
* where the initial constant is assigned an ordinal of zero). */
public final val ordinal: Int
Please note that they are marked final, meaning you can not override them.
So I suggest that instead of trying to change values of these properties as defined by the class, you should declare your own properties in the enum and use their values instead.

what is the meaning of the first line in the below kotlin code. Newbee to Kotlin from Java

I am a Java programmer and new to Kotlin. Please help me understand the below code, especially the first line.
class SiteListEventBus : EventBus<SiteListEventBus.SiteListChangeEvent, String, NotificationHandler<SiteListEventBus.SiteListChangeEvent>>() {
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
override fun getSubscriptionKey(event: SiteListChangeEvent?): String {
return event!!.routingKey
}
}
class SiteListEventBus :EventBus<SiteListEventBus.SiteListChangeEvent, String,
NotificationHandler<SiteListEventBus.SiteListChangeEvent>>() {
So from what im gathering here EventBus would be like your base class which SiteListEventBus is inheriting from and EventBus which conforms to or includes 3 type parameters
Which are SiteListEventBus.SiteListChangeEvent as type 1,
String as type 2,
then NotificationHandler as type 3 which then has a type parameter of SiteListEventBus.SiteListChangeEvent little complicated there
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
This data class then would just be the parameters/variables SiteListChangeEvent which would be your entityId of type string and your routingKey of type string
override fun getSubscriptionKey(event: SiteListChangeEvent?): String {
return event!!.routingKey
}
this last method overrides your getter for subscription key passes in your event which is SiteListChangeEvent? which is an optional value from the ? (so this can be null) to be used and its expecting a String for a return type
then your returning your passed in event!!.routingKey. the not-null assertion operator (!!) converts any value to a non-null type and throws an exception if the value is null.
So, you can write event!!, and this will return a non-null value of event (e.g., a String in your example) or throw a null pointer exception if event is null: soooo this seems like a bad idea because if event is null this will crash for sure
if you need further explanation let me know and ill go into further detail
Here is how I read the first line:
class SiteListEventBus
Define a new class.
: FooBar()
Extend the class FooBar using the empty constructor.
FooBar is actually EventBus<SiteListEventBus.SiteListChangeEvent, String, NotificationHandler<SiteListEventBus.SiteListChangeEvent>>
Generics apply here the way you would expect in Java.
class SiteListEventBus : FooBar() {
Begin implementing the SiteListEventBus class.
Here is how I read the rest:
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
Create a data class.
override fun getSubscriptionKey
The override is similar to the #Override annotation. Override the method getSubscriptionKey.
event!!.routingKey
The event variable is nullable. I recommend reading about the !! operator.

What is the difference between a normal class and a data class in Kotlin?

I tried to resolve task #6 (DataClass) at Kotlin Koans. When I used the normal class in code, the test case failed.
Here's my code of the data class:
data class Person(val name: String, val age: Int)
fun task6(): List<Person> {
return listOf(Person("Alice", 29), Person("Bob", 31))
}
Here's result of the data class:
[Person(name=Alice, age=29), Person(name=Bob, age=31)]
Here's my code of the normal class:
class Person(val name: String, val age: Int)
fun task6(): List<Person> {
return listOf(Person("Alice", 29), Person("Bob", 31))
}
Here's result of the normal class:
[i_introduction._6_Data_Classes.Person#4f47d241, i_introduction._6_Data_Classes.Person#4c3e4790]
Does that mean there is difference between a normal class and a data class in Kotlin. If yes, what is that?
Updated:
Thank #Mallow, you are right. That works:
class Person(val name: String, val age: Int) {
override fun toString(): String {
return "Person(name=$name, age=$age)"
}
}
fun task6(): List<Person> {
return listOf(Person("Alice", 29), Person("Bob", 31))
}
Most of the time we developers use class to keep only data in classes. Classes have some methods which needs to be overridden wrt the data it holds. ex: hashCode(), equals().
Data classes automatically take care of such utilities.
From the official documentation:
We frequently create a class to do nothing but hold data. In such a class some standard functionality is often mechanically derivable from the data. In Kotlin, this is called a data class and is marked as data.
The compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair,
toString() of the form "User(name=John, age=42)",
componentN() functions corresponding to the properties in their order of declaration,
copy() function (see below).
If any of these functions is explicitly defined in the class body or inherited from the base types, it will not be generated.
To read more, check data-classes
About the result, Technically, you are getting is different because of implementation of toString() method. data class' toString() method uses data class properties and values to form returning string. General class' toString() method uses hash code to form returning string.
for a data class.
The compiler automatically derives the following members from all
properties declared in the primary constructor:
equals()/hashCode() pair,
toString() of the form "User(name=John, age=42)",
componentN() functions corresponding to the properties in their order
of declaration,
copy() function (see below).
see https://kotlinlang.org/docs/reference/data-classes.html
A class represents some data "type" and its behaviour(s) so from that point of view data class isn't any different than a class. But there are certain behaviours and rules about a data class that makes it a bit different:
Calling toString() on a data class dumps a string with all its member properties.
It has componentN method that get member properties by their order n.
It has a copy method which takes the member properties as parameters for making a diff copy of the object.
A data class can not be open. Cant be inherited.
It can not be abstract.
It can not be nested, inner or sealed.
Although it can inherit, define abstract methods and implement interfaces.
data class properties can be destructed into individual variables e.g val (name, address) = Person("name", "address")
Pair(a, b) internally uses data class.
It is very common to create classes whose main goal is to hold data. If you want your class to be a convenient holder for your data you need to override the universal object methods:
toString() - string representation
equals() - object equality
hashCode() - hash containers
Note: equals() is used for structural equality and it is often implemented among with hashCode().
Usually, the implementation of these methods is straightforward, and your IDE can help you to generate them automatically. However, in Kotlin, you don't have to general all of these boilerplate code. If you add the modifier data to your class, the necessary methods are automatically added for you.
The return value of toString() will have the format ClassName(parm1=value1, param2=value2, ...). equals() and hashCode() methods take into account all the properties declared in the primary constructor.
The copy() method
When you mark a class as a data class, the method copy() is also automatically generated which allows you to make copies of an existing instance. This feature is very handy when you are using your instances as keys for a HashMap or if you are dealing with multithreaded code.
Even though the properties of a data class are not required to be val, i.e., you can use var, it is strongly recommended that you use read-only properties, so that you make the instances immutable.
Finally, componentN() functions corresponding to the properties in their order of declaration are also generated by the compiler when you mark a class as a data class.
Sample Code
class PersonClass(val name: String, val age: Int)
data class PersonDataClass(val name: String, val age: Int)
>>> val ron = PersonClass("Ron", 18)
>>> val harry = PersonDataClass("Harry", 17)
>>> println(ron) // notice the string representation of a regular class
PersonClass#3b6eb2ec
>>> println(harry) // notice the string representation of a data class
PersonDataClass(name=Harry, age=17)
>>> val harryClone = harry.copy() // this creates a copy of the object referenced by harry
>>> val hermione = PersonDataClass("Hermine", 16)
>>> harry == harryClone
true
>>> harry == hermione
false
In summary, if you need a holder for data, you should use a data class which means adding the modifier data to your class. This will generate the following methods for you: toString(), equals(), hashCode(), componentN(), and copy(), so you avoid writing boilerplate code. If you use a regular class, you won't have all these "batteries included".
Data Class contains internal code which we have to override in Java-like Kotlin generates the equals(), hashCode(), and toString()
Kotlin:
data class User(val name: String, val age: String)
Java:
class Student {
public final String name;
public final String age;
public User(String name, String age) {
this.name = name;
this.age = age;
}
#Override
public boolean equals(Object other) {
}
#Override
public long hashCode() {
}
#Override
public String toString() {
return "User(name=" + name + ",age=" + age + ")";
}
}
Normal Class:
Can be abstract, open, sealed, or inner but not for Data Class
Constructor parameter can be declared without var and val