Use Kotlins copy function with interface - kotlin

I currently have a model similar to:
interface FooInterface {
val param: String
}
data class Foo(override val param: String) : FooInterface
data class Bar(override val param: String) : FooInterface
Classes of the type FooInterface are then stored and modified in a list. For example:
val list: List<FooInterface> = listOf(Foo("abc"), Bar("def"))
list.forEach {
it.copy(param = "test")
}
This is not possible. And while I could understand that this makes sense, because the classes Foo and Bar could have more parameters. Casting would probably work, but this doesn't scale well, when using a lot of classes implementing the interface. But I feel like there is a way to achieve this, that I'm overseeing.

Related

Define an Enum structure in Kotlin?

I have seen a lot of examples of interfaces for enums methods here but I am looking for another thing.
I want to assure some string enums have at least three keys there:
enum InterstitialEnum(val webName: string) {
Showed("interstitialShowed"),
Dismissed("interstitialDismissed"),
Failed("interstitialFailed"),
SomeInterstititalValue("intersititalSomeValue")
}
enum VideoEnum(val webName: string) {
Showed("videoShowed"),
Dismissed("videoDismissed"),
Failed("videoFailed"),
VideoSomethingHere("videoSomethingHere")
}
My end goal is to use that interface as function parameter, so I can access functionParameter.Showed.webName, etc.
I tried to create an interface but I can not find a way to define Showed, Dismissed or Failed, just functions.
This does not work
interface BaseEnum {
val FailedToShow: String;
}
Edit:
Important, this is not a duplicate of How to extend enums in Kotlin? because I do not want the same key/value pair, I want the same key with different value.
You can’t do this with different enums because there’s no mechanism for relating the names of enum instances of different enums.
Here’s an idea for something that’s similar to the structure you’re looking for.
interface WebNames {
val showed: String
val dismissed: String
val failed: String
}
object InterstitialWebNames: WebNames {
override val showed: String = "interstitialShowed"
override val dismissed: String = "interstitialDismissed"
override val failed: String = "interstitialFailed"
}
object VideoWebNames: WebNames {
override val showed: String = "videoShowed"
override val dismissed: String = "videoDismissed"
override val failed: String = "videoFailed"
}
If you have other properties, you could use a wrapper class instead of Strings for these properties.
I think #TenFour04's example is the closest you're going to get. You can think of an enum as a type, and Showed, Dismissed and Failed as subtypes - but there's no way of enforcing that a particular supertype must have a certain set of subtypes, with specific names.
If you don't just want to deal with String properties (e.g. so you can do something like if (state is Showed) then you could make a type for that:
open class State(val webName: String)
class Showed(webName: String) : State(webName)
class Dismissed(webName: String) : State(webName)
class Failed(webName: String) : State(webName)
interface WebNames {
val showed: Showed
val dismissed: Dismissed
val failed: Failed
}
object VideoWebNames : WebNames {
override val showed = Showed("videoShowed")
override val dismissed = Dismissed("videoDismissed")
override val failed = Failed("videoDismissed")
// a State that's not a standard one included in the interface
val videoSomethingHere = State("videoSomethingHere")
}
if you wanted you could stick all the required states in a sealed class, to group them together and maybe do some checking later
open class State(val webName: String)
sealed class RequiredState(webName: String) : State(webName)
class Showed(webName: String) : RequiredState(webName)
class Dismissed(webName: String) : RequiredState(webName)
class Failed(webName: String) : RequiredState(webName)
So now videoWebNames.showed is a State that also is Showed and is RequiredState

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.

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.