【问题标题】:Scala: force type parameter to be a case classScala:强制类型参数成为案例类
【发布时间】:2016-10-23 12:41:57
【问题描述】:

我有一个抽象类 Model 我从中创建案例类:

abstract class Model
case class User(.) extends Model

一个抽象类Table,将Model作为类型参数,用于其默认的具体方法之一:

abstract class Table[M <: Model] {
    def parser = SomeExternalBuilder[M]
}

意思很简单:“根据自己的类,给Table的每个实例一个默认的parser”。

问题是SomeExternalBuilder 只会接受一个案例类作为参数("case class expected: M"),所以它不能编译。 p>

我可以让Table 仅将案例类作为类型参数吗?

我看到一些答案提供了一个缺少的copy 方法(ref1ref2),所以我尝试了这个:

trait Model[T] {
    def copy: T
}

abstract class Table[M <: Model[M]]

但是现在 case class User 扩展 Model[User] 并且必须覆盖 copy Table

没有比在每个孩子身上复制def parser 行更好的方法吗?

编辑:N.B.真正的函数是来自 Play 的“异常”库的def parser: anorm.Macro.namedParser[M]

编辑:此宏检查的类型来源:https://github.com/playframework/anorm/blob/0a1b19055ba3e3749044ad8a54a6b2326235f7c8/core/src/main/scala/anorm/Macro.scala#L117

【问题讨论】:

    标签: scala


    【解决方案1】:

    问题是 SomeExternalBuilder 将只接受一个案例类作为参数(“预期案例类:M”),所以它无法编译。

    我认为您永远无法从 Scala 编译器本身获得这样的消息,这意味着 SomeExternalBuilder.apply 是一个宏。它需要一个 特定 案例类才能知道它的字段,因此您是否可以将 M 限制为案例类(您不能)并不重要:它仍然不会'不接受类型参数。

    可以做的是创建一个宏注解,这样当你写例如

    @HasModel
    class SomeTable extends Table[SomeModel] {
      ...
    }
    

    val parser = namedParser[SomeModel] 是自动生成的。 或者,写入@HasModel[SomeModel] class SomeTable { ... } 并生成extends Table[SomeModel]

    这并不难(就像宏一样),但您仍然需要注释每个扩展 Table 的类。

    【讨论】:

    • 它实际上要求一个用户定义的案例类,我发现它在哪里检查它是否是一个案例类:github.com/playframework/anorm/blob/…。这有帮助吗?
    • 是的,我已经看到您对另一个答案的评论。这正是我的回答所描述的情况。
    • @JulienD 我已经为您的问题添加了一个可能的替代解决方案。
    • 由于它比较短,我更喜欢添加一行函数定义而不是宏调用。但是你的回答是说我想要的是不可能的,这就回答了这个问题。除了我很想知道目前Scala中是否没有这样的功能(“将M限制为案例类”),或者由于理论上的原因根本不可能做到。所以我也可以安静地接受这个答案,知道没有人能带来更多。
    • 目前没有这样的功能,理论上可以添加,但我想不出任何有用的情况(正如我提到的,在这种情况下它实际上没有帮助因为仅仅知道M 是一个案例类并不足以为其生成解析器;您需要知道它是哪个特定类。
    【解决方案2】:

    不是万无一失的解决方案,但值得一试

    case 类扩展 Product 和 Serialisable。约束Product with Serialisable 将帮助您获得一些类型安全。 M 可以是使用 Serialisable 扩展 Product 的任何类。但是Product主要是通过case类扩展的

    abstract class Table[M <: (Product with Serializable)] { 
      def parser = SomeExternalBuilder[M]
    }
    

    【讨论】:

    • 这似乎是无效的语法("identifier expected but '[' found." 在第二个括号中)。也可能是错字:Serializable.
    • @JulienD 括号添加并输入固定
    • @JulienD 修复了编译错误并编辑了答案
    • 不幸的是同样的错误:“案例类预期:M”。我不知道SomeExternalBuilder 是如何测试它作为案例类的,这会有所帮助:( 真正的功能是来自 Play 的 anorm 库的anorm.Macro.namedParser[M]
    • 请注意,这是一个相当松散的近似值。例如,任何元组类型都会满足类型绑定。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-17
    • 2013-01-10
    • 1970-01-01
    相关资源
    最近更新 更多