Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
scala类型类扩展/泛化初始化_Scala_Nullpointerexception_Typeclass - Fatal编程技术网

scala类型类扩展/泛化初始化

scala类型类扩展/泛化初始化,scala,nullpointerexception,typeclass,Scala,Nullpointerexception,Typeclass,我遇到了一个奇怪而困惑的NPE。考虑下面的用例: 编写一个泛型算法(在我的例子中是二进制搜索),其中您希望泛化类型,但需要一些额外的东西 e、 g:也许你想把一个范围一分为二,你需要一个通用的half或two常量 Integraltypeclass是不够的,因为它只提供one和zero,所以我想到了: trait IntegralConsts[N] { val tc: Integral[N] val two = tc.plus(tc.one,tc.one) val four = tc

我遇到了一个奇怪而困惑的NPE。考虑下面的用例:
编写一个泛型算法(在我的例子中是二进制搜索),其中您希望泛化类型,但需要一些额外的东西

e、 g:也许你想把一个范围一分为二,你需要一个通用的
half
two
常量

Integral
typeclass是不够的,因为它只提供
one
zero
,所以我想到了:

trait IntegralConsts[N] {
  val tc: Integral[N]
  val two = tc.plus(tc.one,tc.one)
  val four = tc.plus(two,two)
}

object IntegralConsts {
  implicit def consts[N : Integral] = new IntegralConsts[N] {
    override val tc = implicitly[Integral[N]]
  }
}
并按如下方式使用:

def binRangeSearch[N : IntegralConsts]( /* irrelevant args */ ) = {
  val consts = implicitly[IntegralConsts[N]]
  val math = consts.tc

  // some irrelevant logic, which contain expressions like:

  val halfRange = math.quot(range, consts.two)

  // ...
}
在运行时,这会在这一行抛出一个令人费解的
NullPointerException
valtwo=tc.plus(tc.one,tc.one)

作为一种解决方法,我刚刚在typeclass的
val
s中添加了
lazy
,这一切都解决了:

trait IntegralConsts[N] {
  val tc: Integral[N]
  lazy val two = tc.plus(tc.one,tc.one)
  lazy val four = tc.plus(two,two)
}
但我想知道为什么我会有这种奇怪的NPE。初始化顺序应该是已知的,
tc
应该在到达
valtwo…

初始化顺序应该是已知的,
tc
应该已经 到达
val two时实例化

不符合规格。真正发生的是,在构造匿名类时,首先初始化
IntegralConsts[T]
,然后才在派生的anon类中取消对
tc
的重写,这就是您遇到
NullPointerException
的原因

报告说:

模板评估 考虑一个带有mtn{stats}的mt1的模板
sc

如果这是trait的模板,那么它的mixin计算包括语句序列stats的计算

如果这不是特征的模板,则其评估包括以下步骤:

  • 首先,对超类构造函数
    sc
    进行评估
  • 然后,对模板线性化中的所有基类进行混合求值,直至模板的超类(由
    sc
    表示)。在线性化过程中,Mixin评估的发生顺序与发生顺序相反
  • 最后计算语句序列
    stats
我们可以通过使用
-Xprint:typer
查看编译后的代码来验证这一点:

final class $anon extends AnyRef with IntegralConsts[N] {
  def <init>(): <$anon: IntegralConsts[N]> = {
    $anon.super.<init>();
    ()
  };
  private[this] val tc: Integral[N] = scala.Predef.implicitly[Integral[N]](evidence$1);
  override <stable> <accessor> def tc: Integral[N] = $anon.this.tc
};
正如您所注意到的,由于这是一个匿名类,因此将
惰性
添加到定义中可以完全避免初始化问题。另一种方法是使用早期定义:

object IntegralConsts {
  implicit def consts[N : Integral] = new  {
    override val tc = implicitly[Integral[N]]
  } with IntegralConsts[N]
}

回答得好!谢谢出于某种原因,我的印象是应该急切地实例化抽象的
val
。另一个选项是将
tc
声明为抽象的
def
,并在匿名类中覆盖lazy val
(因为
lazy val
s本质上是
def
s…)@giladhoch很高兴它有帮助!初始化部分可能会有点棘手。我认为我不太喜欢的一点是,所有这些变通方法都依赖于约定,因为其他开发人员可能会被约定所困扰。
object IntegralConsts {
  implicit def consts[N : Integral] = new  {
    override val tc = implicitly[Integral[N]]
  } with IntegralConsts[N]
}