I have the following class structure. The CarModel Class has a defects List which is of Type CarDefects. And I wanted to add the instance of the CarDefects class into this list of defects of carModel which is passed as a parameter for the CarDefects constructor.
However i cannot the use the add method and the error message says the following:
Unresolved reference: add
class CarModel(val brand: Brand, val modelName: String, val version: Int){
var defects: List<CarDefects>? = null
inner class Car(val model: CarModel, val manufactureYear: Int, val engineSerialNum: String){
}
inner class CarDefects(var carModel: CarModel, val affectedYears: Array<Int>, val defectCode: String ) {
init{
carModel.defects.add(//instance of this class)
}
}
}
You have a List as the type of defects. List is immutable, so you can't add more elements to it. You need to use a mutableList to be able to do this.
Here you have more info on this
A generic ordered collection of elements. Methods in this interface support only read-only access to the list; read/write access is supported through the MutableList interface.
Alternatively, you can try to create a new mutable List and add it each time, then convert to list. Something like this.
defects = defects?.toMutableList()?.add(//your car instance).toList()
Related
Example:
data class Car (
val type: TypeEnum,
val brand: BrandEnum,
val modelNumber: Int)
{
constructor(val type: TypeEnum,
val brand: BrandEnum,
val input: String) : this (
type,
brand,
Valdidator.validateModelNumber(input)
)
}
In the code above, the method validateModelNumber() validates a raw input and throws an exception if the model number has an invalid format. I want to force the user to use this constructor every time he/she wants to make a Car object.
Essentially: I want to make sure that no invalid Car object can exist, while still making the code as immutable as possible.
You could use the init block instead. Something like this
data class Car (
val type: TypeEnum,
val brand: BrandEnum,
val modelNumber: Int)
{
init {
Valdidator.validateModelNumber(input)
}
}
Using an init block for validation (as per another answer) can work well if it only needs the parameters/properties specified in the primary constructor. However, there are other approaches.
If you don't want the primary constructor to be used by other code, you can make it private, by changing:
data class Car(
to:
data class Car private constructor(
You could then leave a public secondary constructor for other classes to use, as in the question. However, that's still a bit limiting, as you can't do any serious processing before calling the primary constructor.
So the usual pattern is to have a private constructor and factory methods in the companion object. This is much more flexible: you can do any amount of processing before and after calling the actual constructor; you can even return cached instances, subclass instances, etc.
You can make those look like constructors by implementing them as operator fun invoke() with suitable parameters. In this case, that could look like:
data class Car private constructor(
val type: TypeEnum,
val brand: BrandEnum,
val modelNumber: Int)
{
companion object {
operator fun invoke(type: TypeEnum, brand: BrandEnum, input: String)
= Car(type, brand, Validator.validateModelNumber(input))
}
}
You could then create instances with e.g.:
Car(TypeEnum.SPORTS, BrandEnum.ASTON_MARTIN, "DB5")
looking just like an ordinary constructor.
I need to check if any variables inside of my data class are null. To do this I need retrieve them first but I can't access them directly (e.g. myDataClass.name) because I need it to be generic. Is there a way to access these variables without directly naming them. For example, like accessing a member of an array (myArray[0]).
The mechanism you're looking for is called "reflection" and it allows to introspect objects at runtime. You'll find a lot of information on the internet, but just to give you a link you may want to check this answer.
In your case you could do something like this:
data class MyDataClass(
val first: String?,
val second: String?,
val third: Int?
)
fun main() {
val a = MyDataClass("firstValue", "secondValue", 1)
val b = MyDataClass("firstValue", null, null)
printProperties(a)
printProperties(b)
}
fun printProperties(target: MyDataClass) {
val properties = target::class.memberProperties
for (property in properties) {
val value = property.getter.call(target)
val propertyName = property.name
println("$propertyName=$value")
}
}
Note that for this code to work you must add kotlin-reflect package as a dependency.
I have a simple inheritance tree in my Kotlin project, where a base class is extended by a data class. I cannot declare construction of my data class without overriding the parameters from the base class
I've noticed that this would work if I wasn't extending in a data class:
open class Base(
val first: String,
val second: String
)
class Child(
first: String,
second: String,
val third: List<String>
) : Base(first, second)
This is what I ended up with currently:
open class Base(
open val first: String,
open val second: String
)
data class Child(
override val first: String,
override val second: String,
val third: List<String>
) : Base(first, second)
But I would like to be able not to override the constructor parameters, because I'm not really overriding them. I just need to take them in my Child constructor to be able to pass them to Base.
Having a base class like this and a derived data class, you have to override its properties or separate them, because all primary constructor parameters of a data class must also be declared as properties:
— All primary constructor parameters need to be marked as val or var;
However, based on what your goal really is, you can transform your code in one of the following ways:
Declare the properties in Child as separate, unrelated properties:
open class Base(
open val first: String,
open val second: String
)
data class Child(
val childFirst: String,
val childSecond: String,
val third: List<String>
) : Base(childFirst, childSecond)
This will allow you to have separate implementations for the properties if you need it, storing the values passed as childFirst and childSecond in the Child and probably altering them in some way in the implementation of Base.
Make Base an interface:
interface Base {
val first: String,
val second: String
}
data class Child(
override val first: String,
override val second: String,
val third: List<String>
) : Base
This ensures that Base doesn't have an implementation that stores property values in addition to the Child's properties with backing fields (those will consume additional memory, but, as the propeties are overridden, Base will consistently see the values of the Child's backing fields as first and second).
Make Base an abstract class with abstract properties:
abstract class Base {
abstract val first: String,
abstract val second: String
}
data class Child(
override val first: String,
override val second: String,
val third: List<String>
) : Base()
This follows a similar purpose: Base won't store the property values in its implementation needlessly duplicating the properties of Child.
Make Child a normal class, manually implementing those of the functions that are generated for data classes which you actually need.
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.
Im trying to make universal class and function for different data model classes. My question is how to create ArrayList by Type parameter, type represents each time different class e.g Employee, Warehouse etc.
fun foo(type: Type){
var myList = ArrayList<type>
}
Kotlin has generics, which can be used in the following way:
fun <T> foo() {
val myList = ArrayList<T>()
}
It is important to note that you have to know the type of T at compile time. Otherwise you should probably look at reflection.