Use apply on a data class in Kotlin - kotlin

I know how to use the apply function on a normal Kotlin class but have not been able to use it with a data class:
data class Person(name: String)
val person = Person().apply {
name = "Tony Stark"
}
I get a compile message of:
No value passed for parameter 'name'

The issue is that name is a constructor parameter only and not made a property, which is invalid for the data class concept anyway. Fix like this:
data class Person(val name: String)

The apply function works similar with any class. But there are some errors in your code snippet:
Parameter in Person constructor didn't mentioned as var or val, so there is no fields name in that class. It would be better to make it var to be able to change value.
You made class's constructor with 1 parameter, but trying to use empty constructor - it is error.

Related

Easiest way to modify value passed to inline class constructor

I'm trying to use inline classes in Kotlin to create a class inlining the String class, such that if I have an instance of my class that it will always be true for the contained string that s == s.trim().
I was initially expecting there to be a straightforward way to do this, like perhaps:
#JvmInline
value class Trimmed private constructor(val str: String) : {
constructor(s : String) : super(s.trim())
}
but that doesn't work, and neither do the other direct approaches I considered ("this(s.trim())", etc.).
This problem has turned out to be surprisingly tricky:
Kotlin seems to provide no easy way to have the primary constructor filter or modify the data that is passed to the constructor of the contained String object.
Even if I make the primary constructor private, I can't declare another constructor with the same signature (taking a single String as a parameter).
If this were a normal (non-inlined) class, I could just set the value after superclass class construction (e.g. "init { str = str.trim() }", but since it's an inline class, I can't do that. ("this=this.trim()" doesn't work either, and String objects themselves are immutable so I can't change the contents of 'str'.)
I tried making the class constructor private and creating a factory function in the same file with the same name as the class, but then I couldn't call the class constructor from within the factory function due to access restrictions.
I then tried making the factory function within the class's companion object, but then Kotlin tried to make that function call itself recursively instead of calling the class's constructor. I wasn't able to find a way to syntactially disambiguate this. I managed to work around this by creating a file-private typealias to give another name for the class so I could call the constructor from within the factory function. (Annoyingly, I couldn't declare the typealias in the companion object next to the factory function: I had to declare it outside.)
This worked, but seemed ugly:
typealias Trimmed2 = Trimmed
#JvmInline
value class Trimmed private constructor(val str: String) {
init { assert(str == str.trim()) }
companion object {
// Kotlin won't let me put the typealias here. :-(
fun Trimmed(s: String): Trimmed = Trimmed2(s.trim()) // Don't want recursion here!
}
}
Another working solution is here, using a private constructor with a dummy argument. Of course Kotlin complained that the dummy argument was unused and so I had to put in a big (why is it so big?) annotation suppressing the warning, which is, again, ugly:
#JvmInline
value class Trimmed private constructor(val str: String) {
private constructor (untrimmed: String, #Suppress("UNUSED_PARAMETER") dummy: Unit) : this(untrimmed.trim())
init { assert(str == str.trim()) }
companion object {
fun Trimmed(s: String): Trimmed = Trimmed(s, Unit)
}
}
Is there a simpler, cleaner way to do this? For instance, a syntactic way to clarify to Kotlin that the companion function is trying to call the class constructor and not itself and so avoid the need for a dummy parameter?
Goals:
Code to construct instances of the class from outside this file should look like constructing an instance of a normal class: 'Trimmed("abc")', not using some factory function with a different name (e.g. "of" or "trimmedOf") or other alternate syntax.
It should be impossible to construct the object containing an untrimmed string. Outside code, and the Trimmed class itself, should be able to trust that if a Trimmed instance exists, that its contained str will be a trimmed string.

Cannot access expected class constructor parameters in kotlin multi-platform

I'm currently working on a multi-platform module using kotlin. To do so, I rely on the expect/actual mechanism.
I declare a simple class in Common.kt:
expect class Bar constructor(
name: String
)
I'd like to use the defined class in a common method (also present in Common.kt):
fun hello(bar: Bar) {
print("Hello, my name is ${bar.name}")
}
The actual implementation is defined in Jvm.kt:
actual data class Bar actual constructor(
val name: String
)
The problem is I got the following error inside my hello function
Unresolved reference: name
What am I doing wrong?
Expected classes constructor cannot have a property parameter
Therefore it is necessary to describe the property as a class member with val name: String
Actual constructor of 'Bar' has no corresponding expected declaration
However, for the actual constructor to match the expected declaration the number of parameters has to be the same. That is why the parameter is also added name: String in the constructor in addition to the existence of the property.
expect class Bar(name: String) {
val name: String
}
actual class Bar actual constructor(actual val name: String)
Note: If we leave the constructor empty of the expected class we see how the IDE complains when adding a constructor in the current class for the incompatibility.
GL
It should be val name in the expect part as well, either in the constructor parameter list or as a member property.

DynamoDBMapper load cannot instantiate Kotlin data class

Using DynamoDBMapper within an AWS Lambda (i.e. not Android) written in Kotlin, I can save a record using a data class. However when I attempt to load a record to a data class, I receive a "DynamoDBMappingException: could not instantiate class" exception.
#DynamoDBTable(tableName = "Test")
data class TestItem(
#DynamoDBHashKey(attributeName="someKey")
#DynamoDBAttribute(attributeName = "someKey")
var someKey: String?,
#DynamoDBAttribute(attributeName = "someValue")
var someValue: String?
}
val ddbMapper = DynamoDBMapper(AmazonDynamoDBClientBuilder.defaultClient())
ddbMapper.load(TestItem::class.java, "xyz")
Results in the following exception:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException:
could not instantiate class
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem
With the root exception being:
java.lang.NoSuchMethodException:
com.intuit.connect_to_pro.lambda_common_core.aws_service.TestItem.()
AWS has an example for Android that uses com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper instead of com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper. I tried the Android version, but the result was the same.
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/add-aws-mobile-nosql-database.html
Any help would be appreciated.
The DynamoDBMapper expects a class with an empty constructor. Using a Kotlin data class, you can specify default values for all parameters and use #JvmOverload, which will generate the empty constructor for JVM (Java). Also all parameters need to be mutable, so you need to use "var" instead of "val".
#DynamoDBTable(tableName = "Test")
data class TestItem #JvmOverloads constructor(
#DynamoDBHashKey(attributeName="someKey")
var someKey: String = "",
var someValue: String = ""
)
Make sure that all your classes have an empty constructor. In my case I had nested documents. Those had to have empty constructors too.
In Kotlin, an empty (parameterless) constructor will be created if you specify default values for all the attributes.
Also, make sure that the data from the db can be converted to the data in your classes.
For example, mine failed because I had an Integer property in my class while in the db I had a String. i.e. I had the String value "30" in the db, instead of the Integer value 30.

Why do I need a parameter in a primary constructor without val/var modifier in Kotlin?

If I create a class, I can pass a parameter:
class Person(name: String) {
}
I also can write the same, but with val and then I'll be able to use properties to get this value, when I created an object.
val person = Person("Name")
person.name
The question is: why do I need just a parameter without the val? Where, how and why should I use it?
If you use varor val in the constructor, you are declaring properties and initializing them directly. If you don't, those parameters are used for initialization purposes:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
https://kotlinlang.org/docs/reference/classes.html
It's used when you don't want constructor arguments to become named properties on the class.
If you use val, you get a read-only property. If you use var, you get a mutable property. Otherwise, you get nothing [auto-defined].

Class declarations in Kotlin

I was creating a sample Android project using Kotlin programming language using this blog. I'm new to Kotlin programming. I came across this line,
data class Cats(var data: Data? = null)
which I believe that it is, creating a class named Cats that has a variable named data. What does this data: Data? = null means? My whole class model is:
data class Cats(var data: Data? = null)
data class Data(var images: ArrayList<Image>? = null)
data class Image(var url: String? = "", var id: String? = "", var source_url: String? = "")
You are right that Cats class will have a mutable property named data, since it is declared inside a primary constructor of a class with var keyword (see the docs pages about classes and properties).
Next, question mark at type name in Kotlin means that a variable has nullable type, that is, it can store null value. Variables with not-null types, on the other hand, cannot hold nulls in Kotlin.
data: Data? = null is syntax for default parameter value. It allows not to pass data parameter to the constructor invocation, and in this case default value null will be used. This also works for functions.
And finally, data modifier at class declaration means that equals, hashCode, toString, copy and destructuring will be generated for the class, based on the properties declared in primary constructor.
=> Classes in Kotlin are declared using the keyword class:
class Invoice { }
=> Both the header and the body are optional; if the class has no body, curly braces can be omitted.
class Invoice