【问题标题】:using trait as generic interface in scala在 Scala 中使用 trait 作为通用接口
【发布时间】:2013-05-03 23:15:31
【问题描述】:

我有一个伪泛型类工厂,用于 DAO 目的。运营模式非常简单:
1) 使用特定 DAO 对象“T”类型的知识实例化工厂
2) 然后它创建一个 T
的内部实例 3) 它应该调用 T
的 trait-contracted 函数 4) 最后,返回 T 的实例(假设一切顺利)

看起来像这样:

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 
   }
}

它是这样使用的:
val f = new DAOFactory[DAOTodo]
val shinyNewObject = f.doFoo() // 永远不会到达这里。

'DAOTodo' 实际上是一个实现 DAO 特征的类:

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

问:在“A”点需要做什么才能使用DAO作为“obj”的“doSomethingSmart”函数的接口?

提前致谢。

回答: 除了下面答案中概述的代码修改之外,由于正在创建的类的沿袭(在工厂内)有一个未实现的主构造函数,这并没有按预期运行。创建一个额外的零参数构造函数解决了这个问题。

【问题讨论】:

    标签: scala generics casting traits


    【解决方案1】:

    尝试更改此行:

    val dao = obj.asInstanceOf[DAO] 
    

    到这里:

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

    您不能将 Class[T] 强制转换为 DAO 特征的实例,所以我假设您想要做的是从类类型实例化一个新实例。

    但为了让它更好,你可以这样定义你的工厂:

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

    然后把那一行改成这样:

    val dao = obj.newInstance()
    

    因为您将 T 的类型限制为包含 DAO 特征,所以可以删除那里的演员表。

    把它们放在一起(加上几个小模块),这段代码使用 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)
    }
    

    【讨论】:

    • 编译时会出现警告,“无果类型测试:Class[T] 类型的值也不能是 some.library.DAO”,然后抛出“java.lang.InstantiationException”。我将不得不阅读那个。有任何想法吗? OOC,如何将 scala 特征用于泛型?我见过很多调用已知类的特征的用法,但不是泛型。 Scala 是否会引导用户使用不同的模型? (或者我可能把 scala-ese 的术语弄错了。”
    • 有趣的是,我没有收到任何错误或警告。发布了我的完整代码示例。
    • 嗯。我也在 2.10.0 上。我正在运行这是 scalatra,但我认为这不会影响它,是吗?一个小小的疏忽……我使用的 DAOTodo 类实际上是一个“案例类”。这有关系吗?
    • cmbaxter,谢谢。我仍然无法让它在框架中执行 - 但我确实做了一个提取,比如你的例子,它工作得很好。所以,问题的核心还有其他问题,我正在结束这个问题。欢呼-mjk
    【解决方案2】:

    在我看来,您使用 Manifest 是出于错误的原因。您应该使用反射 Api 中的运行时反射。 Manifest 用于绕过参数化类型的类型擦除。 您需要的在这里解释:http://docs.scala-lang.org/overviews/reflection/overview.html

    【讨论】:

    • 我在类名作为字符串传入的情况下使用了反射,但我认为这种类型会起作用——在某种程度上,它似乎是:类类型在println 是正确的类实例,但我似乎只是可以通过接口/特征访问它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多