简体   繁体   中英

How do I get the runtime Class of a parameterized Type in a Scala trait

I'm trying to implement a Scala trait that handles the details of interfacing with a Java library that requires us to create

What I want to do is something like:

trait SomeTrait[A] extends JavaAPI {
  def foo = {
    callApi(classOf[A])
  }

  override def bar = {
    foo
  }
}

Note that bar is actually overriding a method from a base class, so I can't change it's signature.

I've tried several variations with Manifests, etc., but can't quite get this to work. Is there a way to get the runtime class of a parameterized type?

This flavour should do the trick:

trait SomeTrait[A] {
  def foo(implicit ev: Manifest[A]) = {
    callApi(ev.erasure)
  }
}

update At some point, the manifest must be injected via a method parameter. A constructor would be a good choice, if traits could have them.

Actually, they can! The trait has the constructor of whatever it's mixed-in to, so if you specify an abstract manifest that deriving classes must define...

trait SomeTrait {
  def ev: Manifest[_] //abstract
  def foo = println(ev.erasure)
}

//this `ev` provides the implementation, note that it MUST be a val, or var
class Concrete[T](implicit val ev: Manifest[T]) extends SomeTrait

And all is good again.

You have to get the manifest in there somehow, and traits have no constructor parameters. Only you can say what tradeoff you want to make. Here's another one.

trait SomeTrait[A] {
  implicit def manifesto: Manifest[A]

  def foo = println(manifest[A].erasure)
}
object SomeTrait {
  def apply[A: Manifest] : SomeTrait[A] = new SomeTrait[A] { def manifesto = manifest[A] }
}

Due to type erasure, the compiler has no way to figure out what the type should be within the trait . Thus what you want can't be done. However, you could make it a class. That way the compiler can pass an evidence parameter when an instance is created.

class SomeTrait[A](implicit ev: Manifest[A]) extends JavaApi {
  def foo = {
    callApi(ev.erasure)
  }


  override def bar = {
    foo
  }
}

It might be a little inconvenient to do so in your code, but you can do this

trait SomeTrait[A] extends JavaAPI {
  def objType: Class[A]
  def foo = {
    callApi(objType)
  }

  override def bar = {
    foo
  }
}

object SomeImplementation with SomeTrait[SomeObject] {
  val objType: Class[SomeObject] = classOf[SomeObject]
}

I know it is a little wordy, but that's the way I solved this problem. I hope to find a better solution in the future, but this is what I'm using now. Let me know if that helps you.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM