返回内部类对象的列表

时间:2018-09-26 00:17:15

标签: scala

我从Java到Scala。我想创建一个Outer类,该类定义(1)内部Inner的case类和(2)返回Inner对象列表的方法。请注意,在Java中,内部类的类型将明确为Outer.Inner

我发现有两种方法可以引用内部类的类型:outerObject.InnerOuter#Inner,如here所述。 我应该使用哪个方法返回内部对象的列表?

例如,我可以这样实现我的Scala代码:

// Implementation 1
class Outer {
    case class Inner(id: String)
    def build(input: List[String]): List[Inner] = {
        var result = scala.collection.mutable.ListBuffer[Inner]()
        input.foreach { s =>
            val inner = Inner(s)
            result += inner
        }
        result.toList
    }
}

或类似这样:

// Implementation 2
class Outer {
    case class Inner(id: String)
    def build(input: List[String]): List[Outer#Inner] = {
        var result = scala.collection.mutable.ListBuffer[Outer#Inner]()
        input.foreach { s =>
            val inner = Inner(s)
            result += inner
        }
        result.toList
    }
}

唯一的区别是build()返回List[Inner]List[Outer#Inner]

假设我的代码的调用者运行此代码:

val input: List[String] = List("A", "B", "C")
val outer = new Outer()
val output = outer.build(input)

然后对于实现1,调用者看到的输出是:

output: List[outer.Inner] = List(Inner(A), Inner(B), Inner(C))

在实施2时,调用者看到:

output: List[Outer#Inner] = List(Inner(A), Inner(B), Inner(C))

在Scala中,哪种方法是首选的(惯用的)方法?

相关问题:

1 个答案:

答案 0 :(得分:4)

欢迎来到 Scala

首先,有一种更干净,更有效的方法来创建您的实现,下面将向我展示。

// Implementation 1
class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Inner] = input.map(s => Inner(s))
}

// Implementation 2
class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Outer#Inner] = input.map(s => Inner(s))
}

使用map意味着您不需要任何丑陋的var元素,也不需要构建ListBuffer并将其转换为List

关于哪个更惯用,取决于您要执行的操作。第一种形式返回Inner与路径有关的列表。可以限制它与创建它的Outer实例一起使用。在第二种形式中,返回的Inner的类型不能绑定到特定的Outer实例,但是可以在任何可接受任何 Inner实例的地方使用。因此,后者更具限制性,而前者则更具限制性。请考虑以下内容:

// Implementation 1
class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Inner] = input.map(s => Inner(s))
  def getId(inner: Inner): String = inner.id
}

// Implementation 2
class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Outer#Inner] = input.map(s => Inner(s))
  def getId(inner: Outer#Inner): String = inner.id
}

然后尝试对每个实现进行以下评估:

val input: List[String] = List("A", "B", "C")
val outer = new Outer()
val output = outer.build(input)
val outer2 = new Outer()
outer2.getId(output.head)

使用实现1 ,您会在最后一条语句中看到类型不匹配错误,但不会使用实现2

更新:我忘记提及这一点(可能是避免进一步的混淆),但是与路径有关的版本是常规版本的子类型。 (即this.InnerOuter#Inner的子类型。)因此,您可以将Inner路径依赖版本传递给需要的函数。任何 Inner,但相反。

也就是说,以下将起作用:

class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Inner] = input.map(Inner)
  def getId(inner: Outer#Inner): String = inner.id
}

val input: List[String] = List("A", "B", "C")
val outer = new Outer()
val output = outer.build(input)
val outer2 = new Outer()
outer2.getId(output.head)

但这不会:

class Outer {
  case class Inner(id: String)
  def build(input: List[String]): List[Outer#Inner] = input.map(Inner)
  def getId(inner: Inner): String = inner.id
}

val input: List[String] = List("A", "B", "C")
val outer = new Outer()
val output = outer.build(input)
val outer2 = new Outer()
outer2.getId(output.head)

(请注意,在每种方法中,我还使用了build方法的偶数形式。)

所以,总结一下……

返回新Outer实例的

函数(属于Inner)应返回 path-dependent 形式,因为它也是通用形式的子类型,并提供最大的灵活性。 (但是,请记住,可以从Inner实例的控件外部创建可公开访问的Outer类实例,并且可以是调用者要求的任何类型。)

但是,更重要的是接受现有Inner实例时使用的类型签名:

  • 如果操作(属于Outer实例)在Inner实例与其本身(并且Inner实例通常将保持对其操作的引用)之间具有强耦合Outer实例),因此提供属于不同Inner实例的Outer会是一个错误,那么您应该只接受与路径有关的表格。

  • 但是,如果该操作适用于 any Inner实例,包括其中接受方法根本不属于Outer的情况,则它应该接受通用形式。