Initializing class member to emptyList in secondary constructor - kotlin

I'm very new to Kotlin and have to implement a data class TreeNode that resembles a generic tree of nodes. I'm trying to declare a secondary constructor that initializes the member children to be an empty list. Here's what I tried; but I'm not understanding the syntax quite well so not sure how to go about solving this.
data class TreeNode<T> (
val value: T,
val children: List<TreeNode<T>>,
) {
constructor(value: T, children: emptyList<TreeNode<T>>): this(value){
}
}

You're getting confused by where to put the arguments. If you know the list is empty, you don't need to take it as an argument.
constructor(value: T): this(value, emptyList()) {}

In addition to Silvio's answer, if you want to be able to take a list of children but default to not doing so:
data class TreeNode<T>(val value: T, val children: List<TreeNode<T>> = emptyList()) {}
See the section on default values in constructors in the Kotlin documentation: https://kotlinlang.org/docs/classes.html#constructors

Related

Kotlin: Function declaration must have a name

Aim of the code: Class Pair can print out Product name and quantity, Product name stored in Class Product
class Pair<T, U>(var product: Product, var quantity: Int) {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
Above Error:(2, 9) Kotlin: Expecting member declaration
Error:(2, 57) Kotlin: Function declaration must have a name
class ShoppingCart{
private val productAndQuantityList = mutableListOf<Pair<Product,Int> >()
...
}
open class Product(
val productName: String,
var basePrice: Double,
open val salesPrice: Double,
val description: String) {
...}
may i know how to change my code?
after class Pair was suggested by Compiler, but should i fill in anything?
Which topic should i work for, to avoid the same errors again?
Thanks!
If you want to run the for loop when the object is instantiated, then you should use an initializer. You can't simply put statements inside class definitions directly.
class Pair<T, U>(var product: Product, var quantity: Int) {
init {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
}
However, this code is wrong because Pair does not have access to productAndQuantityList, although ShoppingCart does. As Mathias Henze suggested, you should make a function in ShoppingCart and move the for loop into that, like this:
fun printProducts() {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
As for your Pair class, the type parameters T and U are unnecessary, as you don't use them anywhere, and the class itself is provided by the standard library (The header looks something like data class Pair<out A, out B>(val first: A, val second: B).
If you're determined to use your own Pair class, be sure to make it a data class, so it can be destructured, and change the type of productAndQuantityList to mutableListOf<Pair> (without the type parameters Pair<Product, Int>).
Update
Please read the answer by Mathias Henze, which is correct. My answer, was originally completely wrong, but I have now corrected it.
The productAndQuantityList is just for storing the data. The Pair class is a provided class by Kotlin. You don't need to add anything to it in your usecase.
The ability to print the product and the quantity should be a function of the ShoppingCart, so just have:
class ShoppingCart{
private val productAndQuantityList = mutableListOf<Pair<Product,Int> >()
// ...
fun printContents() {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
}

Kotlin data class secondary constructor init block

Let's imagine that we have data class with two properties and we need secondary constructor for some reasons. Problem is that i need recalculate each argument in primary constructor call instead of using some cached value of raw.split("_"):
data class Id(
val arg1: String,
val arg2: String
) {
constructor(raw: String) : this(raw.split("_")[0], raw.split("_")[1])
}
I can do this in Java but how I can do this in Kotlin?
You can do it this way:
data class Id(
val arg1: String,
val arg2: String
) {
private constructor(splitted: List<String>) : this(splitted[0], splitted[1])
constructor(raw: String) : this(raw.split("_"))
}
It's a good and idiomatic way to solve your problem. Since all secondary constructors must delegate to primary constructor (data class always has it), you can't do what you want in constructor body. In Java it works because there are no primary constructors and no data classes at language level - in Kotlin you can do it like in Java too if you remove data modifier and move properties outside of constructor, but it's a really bad way.

Why should I implement a function type as an interface in Kotlin

I came across something and wondered all the time why you should do this.
You implement an interface in Kotlin through a simple function type:
"It is possible for a class to implement a function type as if it were an interface. It must then supply an operator function called invoke with the given signature, and instances of that class may then be assigned to a variable of that function type:"
class Divider : (Int, Int) -> Double {
override fun invoke(numerator: Int, denominator: Int): Double = ...
}
But why should I do this? Why should I add an interface in that way? I think its only possible to add one function and not more.
Or is it an advantage that I can implement a function with a function body and not only the function head like in normal interfaces? I think it is possible in Java to add default methods to interfaces with a function body. So maybe it is something like that?
Function as a class can have state. For example you could store the last invocations and use the history as a cache:
class Divider : (Int, Int) -> Double {
val history = mutableMapOf<Pair<Int, Int>, Double>()
override fun invoke(numerator: Int, denominator: Int): Double {
return history.computeIfAbsent(Pair(numerator, denominator)) {
numerator.toDouble() / denominator.toDouble()
}
}
}
fun main() {
val divider = Divider()
println(divider(1,2))
println(divider(2,3))
println(divider.history)
}
It is probably not very useful to write a class that only implements a function type interface; however, it might be useful to write a class that can among other things be used in place of a function.
An example from the standard library is the KProperty1 interface. You can write code like this:
data class C(val id: Int, val name: String)
val objs = listOf(C(1, "name1"), C(2, "name2"), C(3, "name3"))
val ids = objs.map(C::id)
Here, C::id is a property reference of type KProperty1<C, Int>, and it can be used as an argument to List.map in place of a lambda because KProperty1<C, Int> extends (C) -> Int. However, KProperty1 has a lot of other uses besides being passed as a function.

Kotlin data classes with Java super class

I have a Java class that holds generic information on databse entities (i.e. their id).
#Data
public class DbEntity {
protected final String id;
public DbEntity(String id) {
this.id = id;
}
}
We use Lombok #Data to generate getters, toString, equals...
In Java I would simply extend this class and add #Data once again.
#Data
class JavaSubClass extends DbEntity {
public JavaSubClass(String id) {
super(id);
}
}
In a newer service we use Kotlin but would like to reuse standard classes such as DbEntity.
My first approach was to simply declare a data class such as
data class SubClass1(val id: String, val name: String) : DbEntity(id)
Accidental override: The following declarations have the same JVM signature (getId()Ljava/lang/String;):
fun <get-id>(): String defined in com.demo.SubClass1
fun getId(): String! defined in com.demo.SubClass1
After some reading I found several solutions, all of which I'm not super happy with.
Don't use data classes. This works but leaves me with the task of implementing equals etc.
class SubClass4(id: String, val name: String) : DbEntity(id)
Duplicate the field. This works but we end up with two fields that could go out of sync.
data class SubClass3(val subId: String, val name: String) : DbEntity(subId)
Assign a different name to the getter. This fundamentally also duplicates the field, but hides the getter.
data class SubClass2(#get:JvmName("getId_") val id: String, val name: String) : DbEntity(id)
As I said, I'm not happy with any of the solution presented above. Having an abstract super class or an interface instead would certainly be more appropriate. However the Entity class resides in a library that primarily Java projects depend on. I'm hesitant to change it just because of a new Kotlin dependnecy.
Did anyone encounter similar issues and has advice on how to solve them?
As a workaround, until KT-6653 - Kotlin properties do not override Java-style getters and setters is fixed, I would go for a variant of your point 3, i.e.:
data class SubClass(#get:JvmName("bogusId") private val id: String, val name: String) : DbEntity(id)
The benefit of this variant is, that you always access the "original" getId-function. You will not use the bogusId()-function as it is not visible/accessible (accessing it via reflection makes no sense... you are only interested in the actual id-field). This works and looks similar for both sides: from Java as also from Kotlin. Still, under the hood this variant uses 2 fields, but in the best case you can just replace it in future with something like:
data class SubClass(override val id: String, val name : String) : DbEntity(id)

Implementing a type-safe class hierarchy w/ a nullable value

I (often) have a resource with two states, pre-created and post-created, where both states have the same fields except for an id field. id is null in the pre-created state and non-null in the post-created state.
I would like to define and use this resource in a clean and type-safe way.
It's common to represent this ID field as a nullable, which handles both scenarios with minimal boilerplate in the class definition. The problem is that it creates a lot of boilerplate in the business logic because you can't assert whether a resource is pre-created or post-created by looking at its type.
Here is an example of the nullable approach:
data class Resource(val id: String?, val property: String)
This is simple to define, but not as simple to handle with due to lack of compile-time guarantees.
Here's an example of a more type-safe approach:
sealed class Resource(val property: String) {
class WithoutID(property: String): Resource(property)
class WithID(val id: String, property: String): Resource(property)
}
This allows me to pass around Resource.WithID and Resource.WithoutID, which have all the same fields and methods, except for id.
One inconvenience with this type-safe approach is that the resource definition code gets quite bloated when you have many property fields. This bloating makes the code harder to read.
I'm wondering if there's an alternative approach with less boilerplate, or if Kotlin has any features that make this kind of thing simpler.
What about defining
sealed class MayHaveId<T> { abstract val record: T }
class WithId<T>(val id: String, override val record: T): MayHaveId<T>()
class WithoutId<T>(override val record: T): MayHaveId<T>()
class Resource(val property: String)
// and other similar types
and using WithId<Resource> and WithoutId<Resource>? In Scala you could add an implicit conversion from MayHaveId<T> to T, but not in Kotlin, alas, nor can you write : T by record. Still should be clean enough to use.
One of the options is to get into composition relying on properties inside interfaces.
interface Resource {
val property: String
}
interface WithId : Resource {
val id: Int
}
interface WithOtherField : Resource {
val otherField: Any
}
class WithoutIdImpl(override val property: String) : Resource
class WithIdImpl(override val id: Int, override val property: String) : WithId
class WithIdAndOtherField(
override val id: Int,
override val otherField: Any,
override val property: String) : WithId, WithOtherField
I didn't get from your example, how you're going to switch between two states of Resource. So probably there is a gap to overcome.
Probably, Smart casts will allow to switch states.