The best way to override a property as a constant in Kotlin - kotlin

Kotlin provides support for property overriding. I an wondering what is the best way to override a property as a constant value.
To be more specific, assume that an abstract val property is declared in a superclass or an interface:
// In a superclass or an interface
abstract val num : Int
In its subclass there are at least 2 ways as far as I can think of to override it:
// In its subclass
override val num : Int = 0
or
// In its subclass
override val num : Int get() = 0
Besides the two, I can also do it in the Java way:
// In a superclass or an interface
abstract fun getNum() : Int
// In its subclass
override fun getNum() : Int = 0
What's the difference among the three in terms of memory and generated bytecode? Which one is the best?
Are there even better ways or patterns in Kotlin to declare properties that are to be overridden as constants?

There's functional difference.
Using assignment You initialize the field when object is created:
override val num : Int = 0
This creates an implicit final backing field with value 0, and getter that always returns same value. This is generated bytecode decompiled into java:
private final int num;
public int getNum() {
return this.num;
}
Second declaration is actually a getter override, which is also a valid way to implement property from the interface. This does not create a backing field, so your property can return different values on each call (for example method call):
override val num : Int get() = 0
Decompiled bytecode:
public int getNum() {
return 0;
}

Related

Kotlin implement `fun` as `val` (Similar to Scala implementing a `def` as `val`)

In Scala you can define a trait with an undefined def to be implemented in the implementing class. Additionally a def can be implemented as a def or as a val.
trait Foo { def bar: Int }
class Baz(val bar: Int) extends Foo // ok
For Context
val has the same meaning in Scala and Kotlin
fun and def are more or less the same in Scala and Kotlin
trait and interface are more or less the same in Scala and Kotlin.
The advantage here is that some implementations of trait can return a static value for bar that is set when the class initializes, and some implementations can generate the value every time bar is accessed.
Is there similar functionality built into Kotlin?
I already tired implementing a fun as a val and that does not work. I currently have the code below, but it feels like there is more boiler plate here than necessary.
interface Foo {
fun bar(): Int
}
class Baz : Foo {
private val _bar: Int = TODO("Some heavy init function.")
override fun bar(): Int = _bar
}
It can be done with a val property instead of a function. When you define a property with just = and no get(), the initialization code is called a single time and assigned to the backing variable, so when the property is accessed, it simply reads the value stored in the backing variable without computing it again:
interface Foo {
val bar: Int
}
class Baz : Foo {
override val bar: Int = TODO("Some heavy init function.")
}
And the interface gives you the flexibility to make it something that is computed on demand (possibly different each time accessed) using a custom getter:
class Baz : Foo {
override val bar: Int get() = Random.nextInt(10)
}
Or computed lazily using a property delegate (the first time it is accessed instead of at class instantiation time):
class Baz : Foo {
override val bar: Int by lazy { TODO("Some heavy init function.") }
}
Or you can upgrade it to be a var and allow external classes to overwrite what is set in the property:
class Baz : Foo {
override var bar: Int = 5
}
Or only internal to the class:
class Baz : Foo {
override var bar: Int = 5
private set
}
With the custom getter, you could do something more complicated involving backing properties, similar to your code with the fun. Or you can design your own property delegate if there's a pattern you use repeatedly.

Kotlin allows defining data class in a function, why?

In kotlin, this is legal:
fun f1(): Int {
data class Data(val i: Int)
val d = Data(0)
return d.i
}
I wonder what are the consequenses of declaring a data class in a function. My best guess is that the data class is scoped to the function but I do not find anything in the doc mentionning that.
This is called Local Classes. They are mentioned in the documentation but only that they cannot have visibility modifiers.
You cannot access local class anywhere outside of the function it was declared in.
It can access any members, including private members, of the containing class.
It can access any local variables or method parameters that are in the scope of the declaring function
You can take a look at Java's local classes for more information. It should be basically the same.
A typical use case is to have a throw-away implementation of some interface.
fun main() {
val f1 = f1()
println(f1.x)
println(f1.y)
}
interface Data {
val x : Int
val y : Int
}
fun f1(): Data {
data class SpecificData(override val x: Int, override val y: Int) : Data
return SpecificData(5, 10)
}

Override getters in Kotlin?

So I have an abstract class Composition, which has two children: one is a Track, and one is an Album (which is a group of Tracks).
class Composition(val name: String, ...)
class Track(name: String): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name)
So far, so good. Now, I have the duration that is added. It is abstract in Composition, so I can override it in the children:
abstract class Composition(...){
abstract fun getDuration(): Int
}
Now, I can add override the method in the Track, which takes it as a parameter:
class Track(..., private val duration: Int): Composition(...){
override fun getDuration() = duration
}
And finally, I make the Album, whose duration is the sum of the Tracks:
class Album(..., val tracks: List<Track>): Composition(...){
override fun getDuration() = tracks.sumBy { it.getDuration() }
}
It works as intended, but I do not understand why I cannot simply use tracks.sumBy { it.duration }, since in Kotlin properties are nothing more than getters and setters (I'm thinking about the getDuration in Composition).
I feel like I'm missing something, because if the same code was written in Java, I would be able to call composition.duration as a property -- so that makes me think that Kotlin allows it from Java code, but not from Kotlin code, which is sad.
An other example:
Let's say I have a class named Artist, who wrote multiple Compositions:
class Artist(
val nom: String,
private val _compositions: MutableList<Composition> = ArrayList()
) {
// HERE (I wrote the extension method List<E>.toImmutableList)
fun getCompositions() : List<Composition> = _compositions.toImmutableList()
}
This is standard in Java (exposing immutable versions of Collections via getters, so they are not modified) ; Kotlin doesn't recognize it though:
val artist = Artist("Mozart")
artist.getCompositions() // Legal
artist.compositions // Illegal
I thought about making this a property, but:
- If I choose the type List<E>, I can override the getter to return the immutable list, but I cannot use regular methods (add...) as the List is immutable
- If I choose the type MutableList<E>, I cannot override the getter to return ImmutableList (which is a subclass of List that I wrote, and is obviously not a subclass of MutableList).
There's a chance I'm doing something ridiculous while there is an easy solution, but right now I cannot find it.
In the end, my question is: Why aren't manually-written getters considered properties when written from Kotlin?
And, if I'm mistaking, What is the expected way of solving both of these patterns?
If you want to use it as property, you should use Kotlin-way to override getter.
For example:
abstract class Composition(...){
abstract val duration: Int
}
// You can use "override" in constructor
// val - is immutable property that has only getter so you can just
// remove private modifier to make possible get it.
class Track(..., override val duration: Int): Composition(...){
...
}
class Album(..., val tracks: List<Track>): Composition(...) {
override val duration: Int
get() = tracks.sumBy { it.duration }
}
Also there are may be case when you need mutable property that can be changed only inside of object. For this case you can declare mutable property with private setter:
class SomeClass(value: Int) {
var value: Int = value
private set
}
Read more in docs: https://kotlinlang.org/docs/reference/properties.html#getters-and-setters
You have to define duration as an abstract property and not as an abtract function (https://kotlinlang.org/docs/reference/properties.html#getters-and-setters):
abstract class Composition(val name: String) {
abstract val duration: Int
}
class Track(name: String, override val duration: Int): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name) {
override val duration: Int
get() = tracks.sumBy { it.duration }
}
The getter/setter conversion as properties does only work for Java classes (https://kotlinlang.org/docs/reference/java-interop.html#getters-and-setters).

why one get "Platform declaration clash" but the other is fine?

Having a interface defined:
interface IData {
fun getHash() : Int
fun getUUID(): UUID
......
}
Trying to create object for the interface. the fun getUUID(): UUID is fine, but the fun getHash() : Int got error as below.
What might be wrong? why they are different?
fun buidlDataList () : ArrayList<IData> {
val dataList = ArrayList<IData>(0)
dataList.add(object : IData {
val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() } //<=== get error
override fun getHash(): Int { //<=== get the same error
return hash
}
val uuid: UUID by lazy { UUID.randomUUID() }
override fun getUUID(): UUID {
return uuid
}
......
}
}
Platform declaration clash: The following declarations have the same JVM signature(getHash() I):
* public final fun <get-hash>(): int defined in com.data. buidlDataList <no name provided>
* public open fun getHash(): int defined in defined in com.data. buidlDataList <no name provided>
The variables create their own getters, but you also explicitly define them. When you declare a var or a val, they usually have their own getters automatically generated1. Private vals or vars don't, if you don't create a custom getter.
But in all other cases, this:
val x: Int = TODO()
generates a getter1.
In your case, I'd recommend using val in the interface directly. You see, the generated getter has the same name as the getHash method you explicitly declared. The getters don't override methods either (unless you annotate it with one of the #Jvm annotations, and I don't remember which, but you don't really need those anyways).
So you change your interface to:
interface IData {
val hash: Int
val uuid: UUID
}
And remove the getters in the overridden object, and add override to the vals:
dataList.add(object : IData {
override val hash: Int by lazy { dataFetchers.size+System.currentTimeMillis().toInt() }
override val uuid: UUID by lazy { UUID.randomUUID() }
}
The first interface is actually equivalent to declaring an interface with get and set methods. If you override it from Java, it will ask you to override getHash() and getUid(), and you need to declare the field locally. Kotlin works differently because it automatically generates setters.
And since you can declare variables in interfaces without messing up Java interop, I highly recommend you use that over #Jvm* annotations (mainly because it makes it easier to understand the code, though that's a personal preference).
Also, if you decompile the Kotlin bytecode, you'll see what the interface with the variables compiles to:
public interface IData {
int getHash();
#NotNull
UUID getUuid();
}
So it's identical to the one you originally had, just without conflicts in child classes because of variable name clashes.
And the reason only one has a clash is because, as you see in the interface, val uuid creates a getter named getUuid, while your interface declares getUUID. Methods in Java and Kotlin are case-sensitive, which is why those don't clash. If you re-name your variable to upper-case UUID, you will get a clash on that too.
1: Assuming the variable/constant isn't in a method. Top-level variables, variables in interfaces, enums, classes, objects, and companion objects all generate getters/setters, but if you declare a variable inside a method, it naturaly won't have getters and setters where that applies.

Kotlin sealed classes assigning property constants

kotlin_version = '1.2.30'
I have a sqlite table that has a Integer value for a column called direction. That will store the Integer property based on the enum constant.
i.e will insert 40 into the table:
saveDirection(Direction.Right.code)
I have a enum class written in kotlin with property constants assigned.
enum class Direction(val code: Int) {
UP(10),
DOWN(20),
LEFT(30),
RIGHT(40),
NONE(0)
}
I am wondering if I can do the same with sealed classes
sealed class Direction {
abstract val code: Int
data class Up(override val code: Int): Direction()
data class Down(override val code: Int): Direction()
data class Left(override val code: Int): Direction()
data class Right(override val code: Int): Direction()
data class None(override val code: Int): Direction()
}
However, this won't work as the saveDirection(direction: Int) is expecting an Int value:
saveDirection(Direction.Right(40))
Is this possible to assign constant properties to sealed classes so that you can get the constant property like in enums?
Thanks for any suggestions,
You could use sealed classes like this:
sealed class Direction(val code: Int) {
override fun equals(other: Any?): Boolean = other is Direction && code == other.code
override fun hashCode(): Int = code
}
class Up : Direction(10)
class Down : Direction(20)
class Left : Direction(30)
class Right : Direction(40)
class None : Direction(0)
However, given the limited context of the question, it is unclear what exactly you would gain by this. In fact, in this simple case, Kotlin does not allow your subclasses to be marked as data class:
Data class must have at least one primary constructor parameter
The solution provided above does not have any advantage over enums, in fact it is more verbose and error-prone than an enum-based definition, so why not just use those?