patternkotlinMinor
Kotlin Data Class Wildcard
Viewed 0 times
dataclasskotlinwildcard
Problem
I'm writing a small custom DSL and interpreter as an exercise in understanding how the language stack (lexer/parser/(interpreter/compiler)) works. Because I want to be able to report the location of semantic errors, the AST needs to retain information about where the elements were in the source.
For this purpose I wrote a
To allow me to write more good unit tests quicker, I decided I wanted to default the position of all my nodes to a wildcard value that would match all valid
I wanted to keep all the
For this purpose I wrote a
data class Point(val line: Int, val column: Int). All well and good, until I realized that manually specifying the position of EVERY node in the AST for unit tests on parsing would be prohibitively time consuming (and error prone). (Obviously, some tests need to do so to test the assigning of positions, but some is a lot fewer than all.)To allow me to write more good unit tests quicker, I decided I wanted to default the position of all my nodes to a wildcard value that would match all valid
Points. The parsed nodes would have the correct position, but the manually assembled AST to check against would not specify (and thus not test) the positions. This is the solution I came up with./**
* DO NOT EXTEND THIS CLASS!!
*
* This is an custom open data class to allow a wildcard value.
*/
open class Point(val line: Int, val column: Int) {
object Wildcard : Point(-1, -1) {
override fun toString() = "Point.Wildcard"
override fun copy() = this
}
override fun equals(other: Any?) = when {
this === other -> true
other === Wildcard -> true
other !is Point -> false
this === Wildcard -> true
line != other.line -> false
column != other.column -> false
else -> true
}
override fun hashCode(): Int { // generated
var result = line
result = 31 * result + column
return result
}
override fun toString() = "Point(line=$line, column=$column)"
operator fun component1() = line
operator fun component2() = column
open fun copy() = Point(line, column)
}I wanted to keep all the
Solution
Your
If you define your own method instead of defining a custom, non-transitive
Notes:
equals method is not transitive (e.g. Point(2, 2) == Point.Wildcard and Point.Wildcard == Point(3, 3) but Point(2, 2) != Point(3, 3). See Any.equals - stdlib - Kotlin Programming Language for more details on the requirements for equals. You might also consider using EqualsTester from guava-testlib for testing your own implementations of equals.If you define your own method instead of defining a custom, non-transitive
equals implementation than your existing solution can become much simpler:data class Point(val line: Int, val column: Int) {
companion object {
val wildcard = Point(-1, -1)
}
infix fun matches(other: Point): Boolean {
return this == other || other === wildcard || this === wildcard
}
}Notes:
Point(2, 2) matches Point(-1, -1)will returnfalse.
Point(2, 2) matches Point.wildcardwill returntrue.
- If you want both to return
truethen replace the===with==inmatches.
- Marking
matchesasinfixis purely optional but it seems to me like a good fit.
Code Snippets
data class Point(val line: Int, val column: Int) {
companion object {
val wildcard = Point(-1, -1)
}
infix fun matches(other: Point): Boolean {
return this == other || other === wildcard || this === wildcard
}
}Context
StackExchange Code Review Q#152403, answer score: 2
Revisions (0)
No revisions yet.