Write a copyable interface more elegant than in Java

I am trying to write an interface that classes can implement to make them "copied", (type) safe Clonable.

In Java, I would do something like this using recursive generics:

public interface Copyable<C extends Copyable<C>> { C copy(); } public class Example implements Copyable<Example> { ... @Override public Example copy() { return new Example(this); //invoke copy constructor } } 

Obviously this is not so elegant, both the Copyable and Example headers look more complex. Is there a more elegant way to achieve this in Kotlin?

+5
source share
1 answer

Here's an attempt to reduce the overall pattern by sacrificing some kind of static type security:

 interface Copyable { fun createCopy(): Copyable } inline fun <reified T : Copyable> T.copy(): T = createCopy() as T 

We can use extension functions to get a generic recipient type without recursive generics. We make an inline extension function to validate the type parameter so that the check is checked and throws an exception if the implementation class does not return an instance of the same type.

Here is a usage example

 class Example(val a: String) : Copyable { constructor(e: Example) : this(ea) override fun createCopy() = Example(this) } fun main(args: Array<String>) { val copiedExample: Example = Example("a").copy() } 

Alternative solution using covariance

Depending on your use case, you do not even need the copy method, since we can use covariance . Declare your types like this:

 interface Copyable { fun copy(): Copyable } class Example(val a: String) : Copyable { constructor(f: Example) : this(fa) override fun copy() = Example(this) } 

And as you can see, the code val copiedExample: Example = Example("a").copy() is still compiling. This is because overridden methods can return a more specific type than a super method, and using the Kotlin single expression functions , the type we want is automatically displayed.

However, this can lead to problems if you are not working with a particular type directly, but are talking about the Copyable bottom interface. The following code does not compile:

 interface CopyableIterable<T> : Iterable<T>, Copyable class Example : CopyableIterable<String> { constructor(e: Example) override fun copy() = Example(this) override fun iterator() = TODO() } fun foo(ci: CopyableIterable<String>) { val copy: CopyableIterable<String> = ci.copy() // error: type mismatch } 

The fix for this is simple, override the copy method in the subinterface as well:

 interface CopyableIterable<T> : Iterable<T>, Copyable { override fun copy(): CopyableIterable<T> } 
+5
source

Source: https://habr.com/ru/post/1267251/


All Articles