Reputation: 843
I'm new to the Scala type system, and I'm trying to explore it via this JAXB Marshalling example. It works if you change the type of the parameter to toString to AnyRef. However, I would like to express via the type system that the parameter to toString must be the same type as the type-parameter to concrete constructor. Is there a way to achieve this?
I don't understand why the error message below seems to indicate that typ = XMLMarshaller[TestObj] instead of just TestObj. In my debugger, typ=TestObj. Any help with this specific question or insight on this piece of code in general is very much appreciated!
error: type mismatch; found : TestObj required: _1.typ where val
_1: XMLMarshaller[TestObj]
val o = new XMLMarshaller[TestObj]().toString(new TestObj("hello","world"))
Here's the code, just paste into REPL:
import javax.xml.bind.{Marshaller, JAXBContext}
import java.io.{ByteArrayInputStream, StringWriter}
import org.jboss.resteasy.plugins.providers.jaxb.json.JettisonMappedContext
import javax.xml.bind.annotation.{XmlRootElement, XmlAccessorType, XmlAccessType}
abstract class XMarshaller {
val context:JAXBContext
type typ <: AnyRef
def toString(obj:typ): String = {
val marshaller:Marshaller = context.createMarshaller()
val sw = new StringWriter
marshaller.marshal(obj, sw)
sw.toString
}
def valueOf(xmlString:String): typ = {
val marshaller = context.createUnmarshaller()
marshaller.unmarshal(new ByteArrayInputStream(xmlString.getBytes())).asInstanceOf[typ]
}
}
class XMLMarshaller[T](implicit mT:Manifest[T]) extends XMarshaller {
val typ = mT.erasure
val context = JAXBContext.newInstance(typ)
}
class JSONMarshaller[T](implicit mT:Manifest[T]) extends XMarshaller {
val typ = mT.erasure
val context = new JettisonMappedContext(typ)
}
@XmlRootElement
@XmlAccessorType(value = XmlAccessType.FIELD)
case class TestObj(x:String, y:String){
def this() {this("","")}
}
object Test {
def main(args: Array[String]) {
val o = new XMLMarshaller[TestObj]().toString(new TestObj("hello","world"))
println(o)
}
}
Upvotes: 1
Views: 511
Reputation: 39356
Because of how Scala's syntax works, a class can have type member and a value member with the same name, and not create any name conflits (you can always tell by the context which is meant).
What you have is analogous to:
abstract class FooAbstract {
type Type <: AnyRef
}
class FooConcrete[T<:AnyRef](implicit mt: Manifest[T]) extends FooAbstract {
val Type = mt.erasure
}
where FooConcrete
does not override the type member Type
. You actually want
class FooConcrete[T<:AnyRef](implicit mt: Manifest[T]) extends FooAbstract {
type Type = T
}
The curious thing is that Scala allows you to fail to override the type, and leave it completely abstract. I'm not sure if this is intentional or a Scala bug.
Upvotes: 2