Alex Vayda
Alex Vayda

Reputation: 6504

Scala: How to invoke method with type parameter and manifest without knowing the type at compile time?

I have a function with the following signature:

myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???

How can I invoke this function if I do not know the exact type of the argument at the compile time?

For example:

val obj: AnyRef = new Foo()    // At compile time obj is defined as AnyRef,
val objClass = obj.getClass    // At runtime I can figure out that it is actually Foo
// Now I would need to call `myFunc[Foo](obj.asInstanceOf[Foo])`,
// but how would I do it without putting [Foo] in the square braces?

I would want to write something logically similar to:

myFunc[objClass](obj.asInstanceOf[objClass])

Thank you!

UPDATE:

The question is invalid - As @DaoWen, @Jelmo and @itsbruce correctly pointed, the thing I was trying to do was a complete nonsense! I just overthought the problem severely. THANK YOU guys! It's too bad I cannot accept all the answers as correct :)

So, the problem was caused by the following situation:

I am using Salat library to serialize the objects to/from BSON/JSON representation. Salat has an Grater[T] class which is used for both serialization and deserialization. The method call for deserialization from BSON looks this way:

val foo = grater[Foo].asObject(bson)

Here, the role of type parameter is clear. What I was trying to do then is to use the same Grater to serialize any entity from my domain model. So I wrote:

val json = grater[???].toCompactJSON(obj)

I immediately rushed for reflection and just didn't see an obvious solution lying on the surface. Which is:

grater[Entity].toCompactJSON(obj)  // where Entity...

@Salat trait Entity                // is a root of the domain model hierarchy

Sometimes things are much easier than we think they are! :)

Upvotes: 4

Views: 2504

Answers (4)

Vladimir Matveev
Vladimir Matveev

Reputation: 128111

It appears that while I was writing this answer the author of the question realized that he does not need to resolve Manifests at runtime. However, in my opinion it is perfectly legal problem which I resolved successfully when I was writing Yaml [de]serialization library, so I'm leaving the answer here.


It is possible to do what you want using ClassTags or even TypeTags. I don't know about Manifests because that API is deprecated and I haven't worked with it, but I believe that with manifests it will be easier since they weren't as sophisticated as new Scala reflection. FYI, Manifest's successor is TypeTag.

Suppose you have the following functions:

def useClasstag[T: ClassTag](obj: T) = ...

def useTypetag[T: TypeTag](obj: T) = ...

and you need to call then with obj: AnyRef as an argument while providing either ClassTag or TypeTag for obj.getClass class as the implicit parameter.

ClassTag is the easiest one. You can create ClassTag directly from Class[_] instance:

useClasstag(obj)(ClassTag(obj.getClass))

That's all.

TypeTags are harder. You need to use Scala reflection to obtain one from the object, and then you have to use some internals of Scala reflection.

import scala.reflect.runtime.universe._
import scala.reflect.api
import api.{Universe, TypeCreator}

// Obtain runtime mirror for the class' classloader
val rm = runtimeMirror(obj.getClass.getClassLoader)

// Obtain instance mirror for obj
val im = rm.reflect(obj)

// Get obj's symbol object
val sym = im.symbol

// Get symbol's type signature - that's what you really want!
val tpe = sym.typeSignature

// Now the black magic begins: we create TypeTag manually
// First, make so-called type creator for the type we have just obtained
val tc = new TypeCreator {
  def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
    if (m eq rm) tpe.asInstanceOf[U # Type]
    else throw new IllegalArgumentException(s"Type tag defined in $rm cannot be migrated to other mirrors.")
}
// Next, create a TypeTag using runtime mirror and type creator
val tt = TypeTag[AnyRef](rm, tc)

// Call our method
useTypetag(obj)(tt)

As you can see, this machinery is rather complex. It means that you should use it only if you really need it, and, as others have said, the cases when you really need it are very rare.

Upvotes: 5

Jelmo
Jelmo

Reputation: 116

It's not clear what you are trying to achieve and a little more context could be helpful. Anyway, here's my 2 cents.

Using Manifest will not help you here because the type parameter needs to be known at compile time. What I propose is something along these lines:

def myFunc[T](arg: AnyRef, klass: Class[T]) = {
  val obj: T = klass.cast(arg)
  //do something with obj... but what?
}

And you could call it like this:

myFunc(obj, Foo.class)

Note that I don't see how you can do something useful inside myFunc. At compile time, you cannot call any method on a object of type T beside the methods available for AnyRef. And if you want to use reflection to manipulate the argument of myFunc, then there is no need to cast it to a specific type.

Upvotes: 2

itsbruce
itsbruce

Reputation: 4843

This is the wrong way to work with a type-safe OO language. If you need to do this, your design is wrong.

myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???

This is, of course, useless, as you have probably discovered. What kind of meaningful function can you call on an object which might be anything? You can't make any direct reference to its properties or methods.

I would want to write something logically similar to:

myFunc[objClass](obj.asInstanceOf[objClass])

Why? This kind of thing is generally only necessary for very specialised cases. Are you writing a framework that will use dependency injection, for example? If you're not doing some highly technical extension of Scala's capabilities, this should not be necessary.

I bet you know something more about the class, since you say you don't know the exact type. One big part of the way class-based OO works is that if you want to do something to a general type of objects (including all its subtypes), you put that behaviour into a method belonging to the class. Let subclasses override it if they need to.

Frankly, the way to do what you are attempting is to invoke the function in a context where you know enough about the type.

Upvotes: 0

DaoWen
DaoWen

Reputation: 33029

This isn't going to work. Think about it this way: You're asking the compiler to create a class Manifest (at compile time!) for a class that isn't known until run time.

However, I have the feeling you're approaching the problem the wrong way. Is AnyRef really the most you know about the type of Foo at compile time? If that's the case, how can you do anything useful with it? (You won't be able to call any methods on it except the few that are defined for AnyRef.)

Upvotes: 3

Related Questions