如何创建可通过参数创建某种类型的子类型实例的Scala函数

时间:2018-11-01 15:48:58

标签: scala

对不起,我对Scala不太熟悉,但是我很好奇这是否可行并且还无法弄清楚该怎么做。

基本上,我想创建一些便利的初始化程序,它们可以生成随机数据样本(在这种情况下为网格)。网格将始终填充有特定类型的实例(在这种情况下为Location)。但是在不同的情况下,我可能希望网格填充Location的不同子类型,例如FarmCity

在Python中,这很简单:

def fillCollection(klass, size):
    return [klass() for _ in range(size)]

class City: pass

cities = fillCollection(City, 10)

我试图在Scala中做类似的事情,但是不起作用:

def fillGrid[T <: Location](size): Vector[T] = {
    Vector.fill[T](size, size) {
        T()
    }
}

编译器只说“找不到:值T”

那么,可以在Scala中近似上述Python代码吗?如果没有,建议如何处理这种情况?我可以为每个子类型编写一个初始化程序,但是在我的真实代码中,它们之间有大量样板重叠,因此,我希望在可能的情况下共享代码。

到目前为止,我想出的最好的解决方法是将一个闭包传递给初始化程序(这似乎是Vectors上的fill方法已经起作用),例如:

  def fillGrid[T <: Location](withElem: => T, size: Int = 100): Vector[T] = {
    Vector.fill[T](n1 = size, n2 = size)(withElem)
  }

这不是一个很大的麻烦,但是让我很好奇为什么Scala不支持“更简单的” Python样式的构造(如果实际上不支持)。我各种各样的明白为什么拥有一个“完全通用”的初始化程序会引起麻烦,但是在这种情况下,我看不到通用初始化所有已知为给定子类型的实例的危害父类型。

3 个答案:

答案 0 :(得分:1)

您可以使用反射来完成您想要的...

这是一个简单的示例,只有在所有子类都具有零args构造函数的情况下,它才起作用。

sealed trait Location
class Farm extends Location
class City extends Location

def fillGrid[T <: Location](size: Int)(implicit TTag: scala.reflect.ClassTag[T]): Vector[Vector[T]] = {
  val TClass = TTag.runtimeClass
  Vector.fill[T](size, size) { TClass.newInstance().asInstanceOf[T] }
}

但是,我从来不喜欢运行时反射,我希望可以有另一种方式。

答案 1 :(得分:1)

Scala无法直接执行此类操作,因为它类型安全。如果您传递没有零参数构造函数的类,则它将不起作用。如果尝试这样做,Python版本会在运行时引发错误。

关闭可能是最好的方法。

答案 2 :(得分:1)

您是正确的,因为您拥有的可能是最简单的选择。 Scala之所以无法以pythonic的方式做事,是因为类型系统要强大得多,并且必须与类型擦除相抗衡。 Scala无法在编译时保证Location的任何子类都具有特定的构造函数,它只能允许您执行它可以保证符合类型的操作(除非您使用反射进行棘手的操作)。

如果要清理一点,可以通过使用隐式使其更像python。

implicit def emptyFarm(): Farm = new Farm
implicit def emptyCity(): City = new City

def fillGrid[T <: Location](size: Int = 100)(implicit withElem: () => T): Vector[Vector[T]] = {
  Vector.fill[T](n1 = size, n2 = size)(withElem())
}

fillGrid[farm](3)

要使其在库中更有用,通常将隐式对象放置在Location的伴随对象中,以便在适当的情况下将它们全部引入范围。

sealed trait Location
...
object Location
{
  implicit def emptyFarm...
  implicit def emptyCity...
}
...
import Location._
fillGrid[Farm](3)