I'm a newcomer to Kotlin/JVM so I'm definitely doi...
# announcements
k
I'm a newcomer to Kotlin/JVM so I'm definitely doing something non-idiomatic ... I'm used to structural equality from functional languages and I've had a few bugs because my classes weren't data classes...
s
This is obviously way overkill and I deserve to be shot for this but:
Copy code
open class Base {
    init {
        check(this::class.isData)
    }
}
🆒 1
👍 2
d
you may also add
fun equals(other: MyType)
in your interrface
k
cool trick, thanks, didn't think of something like that... I'm not used to that kind of "reflection" / runtime checking style...
c
Most likely, you’re not strictly wanting a data class, but a class that properly implements
equals
and
hashCode
. You can put those functions in your interface, which will require implementing classes to define them (and not just use the default ones from
Any
)
Copy code
interface StructuralEquality {
    override fun equals(other: Any?): Boolean
    override fun hashCode(): Int
}

// does not compile
class ClassThatDoesNotImplementEqualsAndHashcode(val a: String) : StructuralEquality {

}

// compiles
class ClassThatImplementsEqualsAndHashcode(val a: String) : StructuralEquality {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as ClassThatImplementsEqualsAndHashcode

        if (a != other.a) return false

        return true
    }

    override fun hashCode(): Int {
        return a.hashCode()
    }
}

// compiles
data class DataClassThatImplementsEqualsAndHashcode(val a: String) : StructuralEquality
👍 3
k
oh, this one looks even better...
didn't know you could put "override" modifier in interfaces...
maybe I should go read a book cover to cover, instead of just winging it 😄
m
Yeah, compile-time checking is the best option! Other than the above solution I was about to suggest writing a Kotlin compiler plugin for your use case, since it’s extendable, but it’s an advanced topic.
k
yeah, I'm gonna leave compiler plugins for another day 🙂
j
In this case, override is required since equals and hashCode are already present on the supertype (Any)
👆 1
k
ironically, i think I'm better of implementing this as reference equality and using singleton object everywhere (a pattern that's not used a lot in FP)
For example, https://kotlinlang.org/docs/reference/interfaces.html doesn't mention you can put override on interface methods (in this way)
it's a quite a nifty trick...
m
yes, very cool!
i
You can put those functions in your interface, which will require implementing classes to define them
This is rather questionable behavior of the compiler and we may change it later. See https://youtrack.jetbrains.com/issue/KT-24971
k
oh-oh 🙂
s
You can get something similar to what you are looking for with sealed classes, but those can only be implemented by classes in the same library. Remember a data class is really just an ordinary class that is final with some synthesized boilerplate. There’s nothing special about them otherwise. The trick with
KClass<*>.isData
is based on Kotlin-specific metadata and is a pretty big antipattern.
👍 1