mjk
mjk

Reputation: 659

using trait as generic interface in scala

I've got a pseudo-generic class factory that I'm using for DAO purposes. The operational model is pretty straightforward:
1) the factory is instantiated with knowledge of a specific DAO-object "T"ype
2) it then creates an internal instance of T
3) it should call a trait-contracted function of T
4) finally, return the instance of T (assuming all goes well)

It looks like this:

class DAOFactory[T](implicit m: Manifest[T]) {
   def obj = m.runtimeClass.asInstanceOf[Class[T]]
   def doFoo(): Option[Any] = {
       val dao = obj.asInstanceOf[DAO]     // (A) this line will crash
       println(obj.toString())    // some.object.called.DAOTodo
       // val bar = dao.doSomethingSmart(now)   <--- ALL DAO-derived classes implement this function
       return Some(obj) // error-catching excluded for brevity 
   }
}

It is used like this:
val f = new DAOFactory[DAOTodo]
val shinyNewObject = f.doFoo() // never gets here.

Where 'DAOTodo' is, in fact, a class that implements the DAO trait:

class DAOTodo extends DAO {
   def doSomethingSmart(when: Whenever) = {...}
}

Q: what needs to be done at point "A" to use DAO as the interface to the "doSomethingSmart" function of "obj"?

Thanks in advance.

ANSWER: Aside from the code modification as outlined in the answer below, this was not running as predicted due to the fact that the lineage for the classes being created (within the factory) had a primary constructor that was not being fulfilled. Creating an additional zero-param constructor resolved the issue.

Upvotes: 0

Views: 2286

Answers (2)

cmbaxter
cmbaxter

Reputation: 35443

Try changing this line:

val dao = obj.asInstanceOf[DAO] 

To this:

val dao = obj.newInstance().asInstanceOf[DAO] 

You can't cast the Class[T] to an instance of the DAO trait, so I'm assuming what you instead wanted to do was instantiate a new instance from the class type.

But to make it even better, you can define your factory like so:

class DAOFactory[T <: DAO](implicit m: Manifest[T]) {

And then change that line to be like this:

val dao = obj.newInstance()

Removing the cast there is possible because you are constraining the type of T to contain the DAO trait.

Putting it all together (with a couple of small mods), this code runs successfully for me using Scala 2.10:

import java.util.Date

object DAOTest{
  def main(args: Array[String]) {
    val fact = new DAOFactory[DAOTodo]
    fact.doFoo
  }
}

class DAOFactory[T <: DAO](implicit m: Manifest[T]) {
   def obj = m.runtimeClass.asInstanceOf[Class[T]]
   def doFoo(): Option[Any] = {
     val dao = obj.newInstance()
     println(obj.toString())
     val bar = dao.doSomethingSmart(new Date) 
     return Some(obj) 
   }
}

class DAOTodo extends DAO {
   def doSomethingSmart(date:Date) = {}
}

trait DAO{
  def doSomethingSmart(date:Date)
}

Upvotes: 5

Harshal Pandya
Harshal Pandya

Reputation: 1624

Seems to me like you're using a Manifest for the wrong reasons. You should be using the Runtime Reflection from reflection Api. Manifest is for a getting around type erasure for parameterized types. What you need is explained here: http://docs.scala-lang.org/overviews/reflection/overview.html

Upvotes: 0

Related Questions