繁体   English   中英

在Scala中,如何对伴随对象执行编译时类型检查?

[英]In Scala, How to perform compile-time type check on companion object?

在许多语言中但在Scala中却不容易做的事情是:

定义原型“ Super”,这样“ Super”的所有实现都必须定义一个构造函数“ create()”。

我发现此约束非常重要,并且能够在运行时识别很多问题。 但是,此功能仅在Java中部分实现(通过定义一个始终会引发错误的“抽象”静态方法),而在Scala中完全缺失(伴侣对象与类完全分离,无法在原型中实现)。

是否有宏或工具允许我执行此操作?

更新抱歉,我的问题是缺少上下文和示例。 这是scala中的正式用例:

在项目A中,我们定义了一个可以被所有子项目扩展的接口:

trait AbstractFoo {}

此接口应始终具有默认的0参数生成器/构造函数,因此项目A可以按需初始化它,但是,每个构造函数的实现对于项目A都是未知的:

object AbstractFoo {

  def default[T <: AbstractFoo: ClassTag](): T
}

因此,问题就变成了:如何严格定义AbstractFoo,以便对于A的所有子项目,AbstractFoo的任何实现都可以:

case class Foo(...) extends AbstractFoo

必须满足:

  1. “ Foo”必须定义一个0参数的构建器/构造器(大概在其伴随对象中)

  2. 调用AbstractFoo.defaultFoo可以调用此0参数生成器/构造函数

应当注意,在替代条件下,存在一种解决方案,该解决方案将每个伴随对象定义为隐式类型类:

trait FooBuilder[T <: AbstractFoo] {
  def default(): T
}

object AbstractFoo {

  implicit object Foo extends FooBuilder[Foo] {
    def default() = {...}
  }

  def default[T <: AbstractFoo: FooBuilder](): T = {
    implicitly[FooBuilder[T]].default
  }
}

这样,如果未定义隐式对象,则编译器将给出未发现的隐式错误(我的代码段可能存在某些语法错误,该想法来自http://www.cakesolutions.net/teamblogs/demystifying-implicits-and-typeclasses -scala

不幸的是,这并不总是很方便,因为A的这个子项目通常对于A项目是未知的。但是默认的隐式生成器无法重新定义,这使得对default()的每次调用都更加复杂。

我相信scala是一种非常可扩展的语言,因此,无论使用宏,注释还是其他元编程技术,都应该至少有一种方法来实施它。 我的问题现在够清楚了吗?

UPDATE2:我相信经过仔细研究Scaladoc之后,我找到了解决方案,在角落里隐藏着一条评论:

如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则选择一个最具体的参数(请参见Scala规范§6.26.4):

...

类型参数的隐式范围(2.8.0)

...

因此,我需要在FooBuilder中编写一个隐式函数:

trait FooBuilder[T <: AbstractFoo] {
  def default(): T

  implicit def self = this
}

object Foo extends FooBuilder[Foo]

所以每次有人打电话:

default[Foo]

scala将引用类Foo的范围,其中包括对象Foo(包含隐式值Foo),并最终找到0参数的构造函数。

我认为此定义比在对象FooBuilder下定义更好,因为您只能定义一次FooBuilder,因此它不是很可扩展。 你同意我的观点吗? 如果是这样,您能不能修改一下答案,以便我给您点积分?

我不明白为什么abstract class甚至Trait都不允许这样做?

abstract class DefineCreate{
  def create(): Unit
}

case class Foo(one: Int)
object Foo extends DefineCreate{
  def create(): Unit =  { Console.out.println("side-effect") }
}

因此,我强迫用户在有问题的objectcreate一个create方法,因为DefineCreate所有实现DefineCreate必须这样做才能进行编译。

更新以下评论

好吧,无需诉诸宏之类的东西,您可以使用类型类实现相同的事情:

trait Constructor[A]{
  def create(): A
}

object Construct{
  def create[A](implicit cr: Constructor[A]): A = cr.create()
}

这并没有明确地迫使伴随对象萌芽方法,但是如果用户想要使用Constructor.create[Foo]模式,它确实会迫使用户创建类型类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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