How to create immutable objects in Kotlin? - kotlin

Kotlin has a const-keyword. But I don't think constants in kotlin are what I think they are. It seems to very different to const in C++. It seems to me that its is only available for static members and to what are primitives in Java and do not compile for class-variables:
data class User(val name: String, val id: Int)
fun getUser(): User { return User("Alex", 1) }
fun main(args: Array<String>) {
const val user = getUser() // does not compile
println("name = ${user.name}, id = ${user.id}")
// or
const val (name, id) = getUser() // does not compile either
println("name = $name, id = $id")
}
As this seems not to work, I think what i really want is a second class, that deletes the operations i don't want to support:
class ConstUser : User
{
ConstUser(var name: String, val id: int) : base(name, id)
{ }
/// Somehow delte the setters here?
}
The obvious downside to such a apprach is that I must not forget to change this class, in case i change User, something that looks very dangerous to me.
But I'm not sure how to do this. So the question is: How does one make immutable objects in ideomatic Kotlin?

The const modifier in Kotlin is used for compile-time constants. Immutability is done with a val keyword.
Kotlin has two types of properties: read-only val and mutable var. vals are equivalent to Java's finals (I don't know how this relates to const in C++, though) and properties or variables declared as such can't change their values once set:
data class User(val name: String, val id: Int)
val user = User("Alex", 1)
user.name = "John" // won't compile, `val` cannot be reassigned
user = User("John", 2) // won't compile, `val` cannot be reassigned
You don't have to hide or delete somehow any setters of val properties as such properties don't have setters.

Related

Best way to Strong-Type a primitive in Kotlin

Following similar patterns in other languages, I would be interested in producing the most useful way to strongly-type a primitive type in Kotlin.
The rationale, of course, is to have two types which are basically primitive (e.g. strings), but which cannot be assignable to each other by mistake.
My latest attempt is given here, and I'm interested to know how can it be minimized further (can defining the derived constructor be omitted?)
abstract class StronglyTyped<T>{
private var value: T
constructor(_value: T) {
value = _value
}
operator fun invoke(): T {
return value
}
}
class UserId: StronglyTyped<String> {
constructor(_value: String): super(_value) {}
}
class UserName: StronglyTyped<String> {
constructor(_value: String): super(_value) {}
}
fun main() {
val a = UserId("this is a userId")
val b = UserName("this is a userName")
var c: UserName
//c = a // <== won't compile
c = b
println(c())
}
Sounds like you're looking for value classes. More information is available in the official documentation.
An example might look something like the following:
value class Password(val value: String)
If you want to enforce some validation on the primitive, you can do so inside the init block.
value class UserId(val value: String) {
init {
require(value.length == 8) { "A userId must be exactly 8 characters long!" }
}
}
Note however, that this just provides compile-time type safety, because the original primitive types are used during the runtime.

How to make field required in kotlin DSL builders

In Kotlin, when creating a custom DSL, what is the best way to force filling required fields inside the builder's extension functions in compile time. E.g.:
person {
name = "John Doe" // this field needs to be set always, or compile error
age = 25
}
One way to force it is to set value in a function parameter instead of the body of the extension function.
person(name = "John Doe") {
age = 25
}
but that makes it a bit more unreadable if there are more required fields.
Is there any other way?
New type inference enables you to make a null-safe compile-time checked builder:
data class Person(val name: String, val age: Int?)
// Create a sealed builder class with all the properties that have default values
sealed class PersonBuilder {
var age: Int? = null // `null` can be a default value if the corresponding property of the data class is nullable
// For each property without default value create an interface with this property
interface Named {
var name: String
}
// Create a single private subclass of the sealed class
// Make this subclass implement all the interfaces corresponding to required properties
private class Impl : PersonBuilder(), Named {
override lateinit var name: String // implement required properties with `lateinit` keyword
}
companion object {
// Create a companion object function that returns new instance of the builder
operator fun invoke(): PersonBuilder = Impl()
}
}
// For each required property create an extension setter
fun PersonBuilder.name(name: String) {
contract {
// In the setter contract specify that after setter invocation the builder can be smart-casted to the corresponding interface type
returns() implies (this#name is PersonBuilder.Named)
}
// To set the property, you need to cast the builder to the type of the interface corresponding to the property
// The cast is safe since the only subclass of `sealed class PersonBuilder` implements all such interfaces
(this as PersonBuilder.Named).name = name
}
// Create an extension build function that can only be called on builders that can be smart-casted to all the interfaces corresponding to required properties
// If you forget to put any of these interface into where-clause compiler won't allow you to use corresponding property in the function body
fun <S> S.build(): Person where S : PersonBuilder, S : PersonBuilder.Named = Person(name, age)
Use case:
val builder = PersonBuilder() // creation of the builder via `invoke` operator looks like constructor call
builder.age = 25
// builder.build() // doesn't compile because of the receiver type mismatch (builder can't be smart-casted to `PersonBuilder.Named`)
builder.name("John Doe")
val john = builder.build() // compiles (builder is smart-casted to `PersonBuilder & PersonBuilder.Named`)
Now you can add a DSL function:
// Caller must call build() on the last line of the lambda
fun person(init: PersonBuilder.() -> Person) = PersonBuilder().init()
DSL use case:
person {
name("John Doe") // will not compile without this line
age = 25
build()
}
Finally, on JetBrains open day 2019 it was said that the Kotlin team researched contracts and tried to implement contracts that will allow creating safe DSL with required fields. Here is a talk recording in Russian. This feature isn't even an experimental one, so
maybe it will never be added to the language.
In case you're developing for Android I wrote a lightweight linter to verify mandatory DSL attributes.
To solve your use case you will only need to add an annotation #DSLMandatory to your name property setter and the linter will catch any place when it is not assigned and display an error:
#set:DSLMandatory
var name: String
You can take a look here:
https://github.com/hananrh/dslint/
Simple, throw an exception if it's not defined in your DLS after the block
fun person(block: (Person) -> Unit): Person {
val p = Person()
block(p)
if (p.name == null) {
// throw some exception
}
return p
}
Or if you want to enforce it at build time, just make it return something useless to the outer block if not defined, like null.
fun person(block: (Person) -> Unit): Person? {
val p = Person()
block(p)
if (p.name == null) {
return null
}
return p
}
I'm guessing your going off this example so maybe address would be the better example case:
fun Person.address(block: Address.() -> Unit) {
// city is required
var tempAddress = Address().apply(block)
if (tempAddress.city == null) {
// throw here
}
}
But what if we wanted to ensure everything was defined, but also wanted to let you do it in any order and break at compile time. Simple, have two types!
data class Person(var name: String = null,
var age: Int = null,
var address: Address = null)
data class PersonBuilder(var name: String? = null,
var age: Int? = null,
var address: Address? = null)
fun person(block: (PersonBuilder) -> Unit): Person {
val pb = PersonBuilder()
block(p)
val p = Person(pb.name, pb.age, pb.address)
return p
}
This way, you get to you the non-strict type to build, but it better be null-less by the end. This was a fun question, thanks.

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.

How can I set the JsName for a property's backing field in Kotlin?

I played about with Kotlin's unsupported JavaScript backend in 1.0.x and am now trying to migrate my toy project to 1.1.x. It's the barest bones of a single-page web app interfacing with PouchDB. To add data to PouchDB you need JavaScript objects with specific properties _id and _rev. They also need to not have any other properties beginning with _ because they're reserved by PouchDB.
Now, if I create a class like this, I can send instances to PouchDB.
class PouchDoc(
var _id: String
) {
var _rev: String? = null
}
However, if I do anything to make the properties virtual -- have them override an interface, or make the class open and create a subclass which overrides them -- the _id field name becomes mangled to something like _id_mmz446$_0 and so PouchDB rejects the object. If I apply #JsName("_id") to the property, that only affects the generated getter and setter -- it still leaves the backing field with a mangled name.
Also, for any virtual properties whose names don't begin with _, PouchDB will accept the object but it only stores the backing fields with their mangled names, not the nicely-named properties.
For now I can work around things by making them not virtual, I think. But I was thinking of sharing interfaces between PouchDoc and non-PouchDoc classes in Kotlin, and it seems I can't do that.
Any idea how I could make this work, or does it need a Kotlin language change?
I think your problem should be covered by https://youtrack.jetbrains.com/issue/KT-8127
Also, I've created some other related issues:
https://youtrack.jetbrains.com/issue/KT-17682
https://youtrack.jetbrains.com/issue/KT-17683
And right now You can use one of next solutions, IMO third is most lightweight.
interface PouchDoc1 {
var id: String
var _id: String
get() = id
set(v) { id = v}
var rev: String?
var _rev: String?
get() = rev
set(v) { rev = v}
}
class Impl1 : PouchDoc1 {
override var id = "id0"
override var rev: String? = "rev0"
}
interface PouchDoc2 {
var id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
}
class Impl2 : PouchDoc2 {
init {
id = "id1"
rev = "rev1"
}
}
external interface PouchDoc3 { // marker interface
}
var PouchDoc3.id: String
get() = this.asDynamic()["_id"]
set(v) { this.asDynamic()["_id"] = v}
var PouchDoc3.rev: String?
get() = this.asDynamic()["_rev"]
set(v) { this.asDynamic()["_rev"] = v}
class Impl3 : PouchDoc3 {
init {
id = "id1"
rev = "rev1"
}
}
fun keys(a: Any) = js("Object").getOwnPropertyNames(a)
fun printKeys(a: Any) {
println(a::class.simpleName)
println(" instance keys: " + keys(a).toString())
println("__proto__ keys: " + keys(a.asDynamic().__proto__).toString())
println()
}
fun main(args: Array<String>) {
printKeys(Impl1())
printKeys(Impl2())
printKeys(Impl3())
}
I got a good answer from one of the JetBrains guys, Alexey Andreev, over on the JetBrains forum at https://discuss.kotlinlang.org/t/controlling-the-jsname-of-fields-for-pouchdb-interop/2531/. Before I describe that, I'll mention a further failed attempt at refining #bashor's answer.
Property delegates
I thought that #bashor's answer was crying out to use property delegates but I couldn't get that to work without infinite recursion.
class JSMapDelegate<T>(
val jsobject: dynamic
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return jsobject[property.name]
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
jsobject[property.name] = value
}
}
external interface PouchDoc4 {
var _id: String
var _rev: String
}
class Impl4() : PouchDoc4 {
override var _id: String by JSMapDelegate<String>(this)
override var _rev: String by JSMapDelegate<String>(this)
constructor(_id: String) : this() {
this._id = _id
}
}
The call within the delegate to jsobject[property.name] = value calls the set function for the property, which calls the delegate again ...
(Also, it turns out you can't put a delegate on a property in an interface, even though you can define a getter/setter pair which work just like a delegate, as #bashor's PouchDoc2 example shows.)
Using an external class
Alexey's answer on the Kotlin forums basically says, "You're mixing the business (with behaviour) and persistence (data only) layers: the right answer would be to explicitly serialise to/from JS but we don't provide that yet; as a workaround, use an external class." The point, I think, is that external classes don't turn into JavaScript which defines property getters/setters, because Kotlin doesn't let you define behaviour for external classes. Given that steer, I got the following to work, which does what I want.
external interface PouchDoc5 {
var _id: String
var _rev: String
}
external class Impl5 : PouchDoc5 {
override var _id: String
override var _rev: String
}
fun <T> create(): T = js("{ return {}; }")
fun Impl5(_id: String): Impl5 {
return create<Impl5>().apply {
this._id = _id
}
}
The output of keys for this is
null
instance keys: _id
__proto__ keys: toSource,toString,toLocaleString,valueOf,watch,unwatch,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__,__proto__,constructor
Creating external classes
Three notes about creating instances of external classes. First, Alexey said to write
fun <T> create(): T = js("{}")
but for me (with Kotlin 1.1) that turns into
function jsobject() {
}
whose return value is undefined. I think this might be a bug, because the official doc recommends the shorter form, too.
Second, you can't do this
fun Impl5(_id: String): Impl5 {
return (js("{}") as Impl5).apply {
this._id = _id
}
}
because that explicitly inserts a type-check for Impl5, which throws ReferenceError: Impl5 is not defined (in Firefox, at least). The generic function approach skips the type-check. I'm guessing that's not a bug, since Alexey recommended it, but it seems odd, so I'll ask him.
Lastly, you can mark create as inline, though you'll need to suppress a warning :-)