Ezhik
Ezhik

Reputation: 874

Pass type information at runtime

I have a DSL and some runtime code. The problem is I got somewhere at runtime:

val clazz: Class[_ <: java.io.Serializable] = classOf[java.lang.String]
val value: java.io.Serializable = "abc"

and I have a class

class MyWrapper[T <: java.io.Serializable](val data: T)(implicit m: Manifest[T]) {
  override def toString = m.runtimeClass
}
val wrapper = new MyWrapper(value)

The problem is I need to return java.lang.String from a call of toString. But I got java.io.Serializable. Unfortunately I am neither able to create fixed pattern matching for each java.io.Serializable subtype (this would be crazy) nor to create MyWrapper explicit with new MyWrapper[String](value). I don't know the type of value, maybe it is a subtype of Serializable.

Is there some way to pass type/manifest value at runtime if I know that value type is equal to variable clazz?


Update (solution from Régis Jean-Gilles don't work). REPL test:

val clazz: Class[_ <: java.io.Serializable] = classOf[java.lang.String]
val value: java.io.Serializable = "abc"

class MyWrapper[T <: java.io.Serializable](val data: T)(implicit m: Manifest[T]) {
  override def toString = m.runtimeClass.getName
}
val wrapper = new MyWrapper(value)
//val wrapper = new MyWrapper(value)(ClassManifest.fromClass(clazz).asInstanceOf[ClassTypeManifest[java.io.Serializable]])
//val wrapper = new MyWrapper(value)(ClassManifest.fromClass(clazz))
System.err.println(wrapper.toString)

I got an error, if I tried to pass manifest explicit:

scala> :load test.scala
Loading test.scala...
clazz: Class[_ <: java.io.Serializable] = class java.lang.String
value: java.io.Serializable = abc
defined class MyWrapper
<console>:10: error: type mismatch;
 found   : scala.reflect.ClassManifest[_$1] where type _$1 <: java.io.Serializable
    (which expands to)  scala.reflect.ClassTag[_$1]
 required: Manifest[java.io.Serializable]
       val wrapper = new MyWrapper(value)(ClassManifest.fromClass(clazz))
                                                                 ^
<console>:8: error: not found: value wrapper
              System.err.println(wrapper.toString)

Also I am unable to cast manifest explicit.

There is more strange error when I try to compile my application -

[error]  found   : scala.reflect.ClassManifest[(some other)_0(in method setProperty)]
[error]     (which expands to)  scala.reflect.ClassTag[(some other)_0(in method setProperty)]
[error]  required: Manifest[_0(in method setProperty)]
[error]           new Value.Static(default, Context.virtual(element))(ClassManifest.fromClass(elementProperty.typeClass)))

IMHO Régis Jean-Gilles very very close to solution. How to make it work with Scala 2.10?

Upvotes: 2

Views: 1254

Answers (2)

R&#233;gis Jean-Gilles
R&#233;gis Jean-Gilles

Reputation: 32729

If I understand correctly, you are using manifests to work around type erasure but at a specific point all you have is a Class[_] so you need to convert it back to a manifest. Correct?

If so, you can use ClassManifest.fromClass, and then explicitly pass it as the implicit value m.

val wrapper = new MyWrapper(value)(Manifest.classType(clazz))

You should probably abstract it away:

object MyWrapper {
  def fromRuntimeType[T <: java.io.Serializable]( value: T ): MyWrapper[T] = {
    new MyWrapper(value)(Manifest.classType(value.getClass))
  }
}

Keep in mind though that because you only have a Class instance in the first place, you are at the mercy of type erasure again. This means that if the class (that you need to get a manifest for) is generic, you won't have any type parameter information in the manifest returned by ClassManifest.fromClass.

Upvotes: 2

Malte Schwerhoff
Malte Schwerhoff

Reputation: 12852

Wouldn't

override def toString = data.getClass.getName

already work for you? That yields java.lang.String.

Upvotes: 0

Related Questions