在Scala中,如何使抽象类型的内部类型对于不同的实例不变?

时间:2019-03-04 00:26:10

标签: scala singleton inner-classes

在某些情况下,我想使用内部类型作为通用类型的替代,例如在以下情况下,我没有定义Dependent[T <: BaseType],而是这样定义:

abstract class BaseType {

  type N
  def create(v: Int): N

  class Dependent(val n: N) {

    def +(other: Dependent) = new Dependent(create(n.hashCode() + n.hashCode()))
  }
}

object BaseType {

  class Aggregator[T <: BaseType](val seq: Seq[T#Dependent]) {

    def result() = seq.reduce(_ + _)
  }
}

很显然,这不会编译,因为Dependent现在是一个内部类,并且在不同BaseType实例下创建的不同Dependent不能互操作。 (顺便说一句,将引发以下编译错误)

Error:(15, 35) type mismatch;
 found   : T#Inner
 required: _33.Inner where val _33: T
    def result() = seq.reduce(_ + _)

但是这个问题应该在scala中有一个简单的解决方案,因为在Java中,这样的任务可以很容易地通过以下方式完成:

static class Dependent { ...

在scala中编写比Java更长的代码没有任何意义。 scala中的直接类比是在BaseType中添加一个强制规则,该规则要求其所有实现都是对象/单个而不是类。但是我还没有看到这样的功能。

问题是,用Java可以轻松完成同一件事的最短方法是什么?

更新显然我的意图不是很清楚,我想要的不是按原样使用类Dependent,而是扩展BaseType使得BaseType可以用作Dependent的类型参数,例如如果我定义2个对象:

  object Sub1 extends BaseType {
    override type N = Long
    override def create(v: Int): N = v.toLong
  }
  object Sub2 extends BaseType {
    override type N = Double
    override def create(v: Int): N = v.toDouble
  }

然后+符号和Aggregator只能在2个从属对象上使用,只要它们来自同一对象,这将成功:

  assert(
    new Aggregator(
      Seq(
        new Sub1.Dependent(1),
        new Sub1.Dependent(2)
      )).result() == new Sub1.Dependent(1) +
      new Sub1.Dependent(2)
  )

但是这将失败:

  assert(
    new Aggregator(
      Seq(
        new Sub1.Dependent(1),
        new Sub2.Dependent(2)
      )).result() == new Sub1.Dependent(1) +
      new Sub2.Dependent(2)
  )

因为2个从属实例的类型不同,但是对于最新的scala编译器,即使第一个实例也会失败,因为在定义Aggregator时,它并不知道T <: BaseType是单例。

2 个答案:

答案 0 :(得分:3)

  

因为在Java中,这样的任务可以很容易地通过以下方式完成:

static class Dependent { ...

否,它无法完成任务,因为此Dependent不能使用N(即使它是BaseType的类型参数)。您需要static class Dependent<N>,而Scala等效项是

object BaseType {
  class Dependent[N]
}

如果您还没有伴侣对象,这的确比Java长一点,但这只是Scala设计中的一项要求,即它可以用较短的代码来完成Java可以做的任何事情(或根本做到)。

如果需要

  

然后+符号和Aggregator都只能在2个从属对象上使用,只要它们来自同一对象,这将成功

这可以做到:

class Aggregator[T <: BaseType with Singleton](val seq: Seq[T#Dependent]) {
  def result() = seq.reduce(_ + _)
}

答案 1 :(得分:0)

我不确定这是否是您想要的,可能是您过分简化了实际问题。
但是,如果没有,这是否可以解决您的问题?

sealed trait BaseType[N] {
  def create(v: Int): N
}

object BaseType {
  final implicit val LongBase: BaseType[Long] = new BaseType[Long] {
    override def create(v: Int): Long = v.toLong
  }

  final implicit val DoubleBase: BaseType[Double] = new BaseType[Double] {
    override def create(v: Int): Double = v.toDouble
  }

  final class Aggregator[N : BaseType](val seq: Seq[Depedent[N]]) {
    def result: Depedent[N] = seq.reduce(_ + _)
  }
}

final class Depedent[N](val n: N)(implicit base: BaseType[N]) {
  def + (that: Depedent[N]): Depedent[N] =
    new Depedent(base.create(this.n.hashCode + that.n.hashCode))
}

如果这不能解决您的问题,请留下评论以阐明为什么不能解决问题。