0__
0__

Reputation: 67280

Getting around a type-erasure in pattern-matching

I am trying to work around a type erasure in pattern matching. Assuming:

import java.io._

trait Serializer[V] {
  def save(os: OutputStream, v: V): Unit
  def load(in: InputStream): V
}

trait HasSerializer[V] { def serializer: Serializer[V] }

How can I get this to compile without warning and without asInstanceOf:

def test[V](os: OutputStream, v: V): Unit = v match {
  case hs: HasSerializer[V] => hs.serializer.save(os, v)
  case _                    => ???
}

? test is called with a value from a map and there is no means to provide a class manifest.

Any fancy extractor trick maybe?

Upvotes: 6

Views: 617

Answers (2)

0__
0__

Reputation: 67280

Well the question has kind of a wrong precondition (as I just realize) -- we can take Serializer apart into a serializer and a deserializer. Obviously, when I have an instance of V, my use case is serialization, and that doesn't require V as a return type. Thus

trait Serializer { def save(os: OutputStream): Unit }

would suffice, and any type can mix that in. And do:

def testSer[V](os: OutputStream, v: V): Unit = v match {
  case s: Serializer => s.save(os)
  case _ => new ObjectOutputStream(os).writeObject(v)
}

And for deserialization, we would either provide the deserializer along with the construction of a Ref[V], or rely on class lookup through ObjectInputStream.

Upvotes: 2

Alex Cruise
Alex Cruise

Reputation: 7979

If you can make Serializer an abstract class instead, you can give it a Manifest as an implicit constructor parameter, and use that to get the concrete class at construction, then use it later for dynamic type checks.

import java.io._

abstract class Serializer[V: Manifest] {
  def save(os: OutputStream, v: V): Unit
  def load(in: InputStream): V
  val clazz = manifest[V].erasure
}

val ser = new Serializer[Int] {
  def save(os: OutputStream, v: Int) {
    os.write((v.toString + "\n").getBytes)
  }

  def load(in: InputStream) = {
    val line = new BufferedReader(new InputStreamReader(in)).readLine()
    line.toInt
  }
}

ser.clazz //  java.lang.Class[_] = int

Upvotes: 4

Related Questions