我从Java到Scala。我想创建一个Outer
类,该类定义(1)内部Inner
的case类和(2)返回Inner
对象列表的方法。请注意,在Java中,内部类的类型将明确为Outer.Inner
。
我发现有两种方法可以引用内部类的类型:outerObject.Inner
或Outer#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中,哪种方法是首选的(惯用的)方法?
相关问题:
答案 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.Inner
是Outer#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
的情况,则它应该接受通用形式。