I'm writing a DSL where I ultimately want to be able to have my own string type where I can do things like
var s:BString = "123"
if ("123" == s) ...
and also
var d:Double = s + 5.0
I have addition basically working with implicit conversions along with 5.0 + s
I also have == working one way by overriding the equals method in my 'BString' class ,where the first argument (left side) is a BString.
The problem is somehow overriding the Java String equals. I had a look at the String.equals() source code, and the equals method takes a Java Object which I can't seem to get working with implicit conversions. The equals method there then casts the object to a string, so I think unless I have a subclass of (final) String I'm SOL.
Is there any other approach? Ideas?
class BString(val string: String) {
override def toString() = string
def toDouble:Double = string.toDouble
def +(bs:BString) = string.toDouble + bs.toDouble
def +(d:Double) = string.toDouble + d
override def equals(x$1:Any):Boolean = string == x$1.toString
}
object Test {
implicit def string2BString(x:String) = new BString(x)
implicit def double2BString(x:Double) = new BString(x.toString)
implicit def bString2Object(x:BString) = { // never being reached
println("OO");
x.asInstanceOf[Object]
}
def main(args: Array[String]) {
var y:BString = "1.1"
println(y + 1.1) //2.2
println(1.2 + y) //2.3
println("1.1" == y) // false :-(
println("1.1" equals y) // false :-(
println("1.1" equals bString2Object(y)) // also false
println(y == "1.1") // true
}
}
As much as I don't like implementing language behavior like adding strings to doubles (and vice versa), here's what you could do:
import scala.language.implicitConversions
class BString(val string: String) {
def toDouble: Double = string.toDouble
def +(bs: BString): Double = string.toDouble + bs.toDouble
def +(d: Double): Double = string.toDouble + d
override def equals(other: Any) = other match {
case bs: BString => string == bs.string
case os: String => string == os
case _ => false
}
}
object BString {
implicit def string2BString(x: String) = new BString(x)
implicit def double2BString(d: Double) = new BString(d.toString)
}
object Test extends App {
val y: BString = "1.1"
println(y + 1.1) // 2.2
println(1.2 + y) // 2.3
println(("1.1": BString) == y) // true
println(("1.1": BString) equals y) // true
println(y == "1.1") // true
}
As you can see, I've changed both the definition of equals
such that it pattern matches on the runtime type of its argument, and I've also given the typer a hint that "1.1" is really a BString
by writing ("1.1": BString)
.
EDIT: Also note that you actually don't need the def +(d: Double)
method.
String
is a final class in the JDK for lots of reasons - basically you could compromise the entire security model of the JVM if you were allowed to fiddle with its equals
method! (See this question for detailed reasons.)
That being so, the best you will be able to do in your DSL is introduce a new operator that is not defined for String
, for example ===
, and make sure you have an implicit conversion available for String
to BString
:
class BString(val string: String) {
def ===(other: BString) = TODO
}
object BString {
implicit def string2BString(x: String) = new BString(x)
}
You might also consider making your BString
class final as well. Writing a correct equals
method can be difficult or impossible in the face of inheritance - think about the asymmetry that you have already witnessed when trying to compare String
and Object
, and check out this old article for a thorough treatment of the problem.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.