2

I would like to semantically constrain a map to only accept "data" class object types as the value in kotlin like so:

class Test(
    val test : Int
)

data class Test2 (
    val test : Int
)

fun test(map : Map<String, /* compile error on Test, but accept Test2 or any other data class */>) {
}

I'm mainly trying to do this so that I can keep everything in the map cloneable, but when I do this:

fun <T: Cloneable> test(map : Map<String, T>) {
     // test clone
     map.map { it.key.uuid to it.value.clone() } .toMap() // error on .clone() Cannot access 'clone': it is protected in 'Cloneable'
}

but I thought implementing the Cloneable interface made your clone method public? Essentially I'm looking for a compile time guarantee that all data is copyable in that method invocation, (is a primitive type, a data class that I can call .copy() on, or any object that has implemented Cloneable). Is my only option reflection and runtime assertions?

FatalCatharsis
  • 3,407
  • 5
  • 44
  • 74
  • You can use reflection to call hided functions. In this way you can keep compile time check. But it could be not better solution then mentioned reflection check. :) – Ircover Feb 26 '19 at 10:50
  • Implementing `Cloneable` does not make the `clone` method public; see the Javadoc: https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html – Erwin Bolwidt Feb 26 '19 at 11:13

2 Answers2

1

I don't know whether this is the best way or not, but how about you to use property like below.

SomeClass::class.isData

Kdoc says

true if this class is a data class.

Choim
  • 372
  • 1
  • 10
  • 1
    That's what I mean by reflection being my only option. I can check at runtime that an `Any?` is a data type with that property or if it implements `Cloneable` with `is`, but I'd rather use a generic constraint of some kind to ensure that the map type I'm passing to the function is only a valid cloneable values _at compile time_. If that isn't possible, I'll just incur the minute runtime cost of reflection like in your answer. – FatalCatharsis Feb 26 '19 at 06:41
1

I thought implementing the Cloneable interface made your clone method public?

No, it's simply a marker interface, which tells the protected Object.clone() method not to throw a CloneNotSupportedException.  In practice, classes that implement Cloneable will usually override clone() and make it public, but that's not necessary.  And of course that's no help when you don't know the exact type!

The cloning mechanism was an early part of Java, and not very well-designed.  (Effective Java calls it “a highly atypical use of interfaces and not one to be emulated”.)  But it's still used, so we're stuck with it…

(See also these related answers.)

gidds
  • 16,558
  • 2
  • 19
  • 26
  • Interesting, thanks for the reading material. what I was looking for was a deep clone which `.clone()` doesn't always have to fit the bill for anyway. Looking into that, it looks like the only good way to deep clone is via serialization? Seems kinda slow but I found some articles that had suggested performance improvements. That will likely not be the bottle neck of what I'm trying to do. Now I intend to semantically constrain to `Serializable` types, which technically makes sense since I'm serializing data as a message between threads. Thanks for pointing me in the right direction. – FatalCatharsis Feb 27 '19 at 02:18
  • Using serialisation is also ugly.  If these types are under your control, how about writing your own interface with a `deepCopy()` method?  You can implement that to do the right thing, and your map can specify it. – gidds Feb 27 '19 at 09:28
  • I would definitely do that, but I don't have control over the objects passed in. It also needs to accept primitives and any other data pojo. – FatalCatharsis Feb 27 '19 at 15:58