具有继承的Scala中的字段初始化和构造函数序列

时间:2017-02-20 13:26:47

标签: scala inheritance

假设我有一个类Graph和一个子类MyGraphGraph有一个方法build,允许子类定义自己的构建过程。

MyGraph中,我声明了一个字段node,它应该是构建方法中的init。因此我宣布它为null。但是,当我创建MyGraph实例时,执行序列首先转到MyGraph.build然后转到var node : Node = null,这会使节点为空。

在从父级调用的方法中初始化这样一个字段的正确方法是什么?

class Graph {
    def build:Unit = Unit
    build
}

class MyGraph extends Graph {
    var node : Node = null
    override def build:Unit = {
        node = new Node
    }
}

[编辑]关于我的用例的更多细节:Graph类表示计算图,其包含用于计算任务的节点。该图有一些输入和一个输出。在子类中,我需要公开输入节点以供用户提供输入数据。这里有一些代码提供了更多细节。

class Graph {
    val inputs = new ArrayBuffer[InputNode]()
    var output: Node = null

    def build:Unit = Unit
    build

    def newInput(): InputNode = {
        val in = new InputNode()
        inputs += in
        in
    }

    def setOutput(out: Node) {
        this.output = out
    }

    def compute():Unit = {
        inputs.foreach(_.computeAndForward())
    }
}

class LinearRegGraph extends Graph {
    var w : InputNode = null
    var x : InputNode = null
    var b : InputNode = null

    override def build:Unit = {
        w = newInput()
        x = newInput()
        b = newInput()
        val mul = new MulNode(w,x)
        val add = new AddNode(mul, b)
        setOutput(add) 
    }
}

object Main extends App {
    val graph = new LinearRegGraph()

    graph.x.setData(...)
    graph.w.setData(...)
    graph.b.setData(...)

    graph.compute()
    graph.output.getData()
}

我目前使用以下临时解决方案。但是,此代码很容易受到节点构建序列的影响,我不喜欢它。

class LinearRegGraph extends Graph {
    def w = inputs(0)
    def x = inputs(1)
    def b = inputs(2)

    override def build:Unit = {
        val w = newInput()
        val x = newInput()
        val b = newInput()
        val mul = new MulNode(w,x)
        val add = new AddNode(mul, b)
        setOutput(add) 
    }
}

2 个答案:

答案 0 :(得分:1)

你应该真的避免在Scala中使用Java的null

如果您的字段未定义,或者在您的情况下,如果您只是希望您的字段等待初始化并且该字段没有任何风险,则应该更喜欢使用Option[Node]在初始化之前访问,您可以使用以下语法:

class MyGraph extends Graph {
  var node: Node = _
  override def build: Unit {
    node = new Node
  }
}

来源:alvinalexander.com

答案 1 :(得分:1)

首先,我建议您避免使用var并尝试重构代码,以便使用val来使用更具功能性的方法。

现在代码是,我能想到的唯一解决方案如下:

trait Graph

case class MyGraph(node: Node) extends Graph

val myGraph = MyGraph(new Node)

但是有关您的具体问题的更多细节可能会产生更合适的解决方案。

编辑:您可以执行以下操作

abstract class Graph {
    val inputs = new ArrayBuffer[InputNode]()
    val output: Option[Node] = None

    def newInput(): InputNode = {
        val in = new InputNode()
        inputs += in
        in
    }

    def compute():Unit = {
        inputs.foreach(_.computeAndForward())
    }
}

class LinearRegGraph extends Graph {
    val w: InputNode = newInput()
    val x: InputNode = newInput()
    val b: InputNode = newInput()

    val output = {
        val mul = new MulNode(w,x)
        Some(new AddNode(mul, b))
    }
}

object Main extends App {
    val graph = new LinearRegGraph()

    graph.x.setData(...)
    graph.w.setData(...)
    graph.b.setData(...)

    graph.compute()
    graph.output.get.getData()
}

我删除了build方法,并使用构造函数来初始化值。我还将输出节点初始化为None中的GraphSome(add)中的LinearRegGraph

如果您不想使用Option,因为Graph永远不会有输出,您可以尝试以下操作:

trait Graph {
    val inputs = new ArrayBuffer[InputNode]()
    def output: Node

    def newInput(): InputNode = {
        val in = new InputNode()
        inputs += in
        in
    }

    def compute():Unit = {
        inputs.foreach(_.computeAndForward())
    }
}

class LinearRegGraph extends Graph {
    val w: InputNode = newInput()
    val x: InputNode = newInput()
    val b: InputNode = newInput()

    override def output = {
        val mul = new MulNode(w,x)
        new AddNode(mul, b)
    }
}

object Main extends App {
    val graph = new LinearRegGraph()

    graph.x.setData(...)
    graph.w.setData(...)
    graph.b.setData(...)

    graph.compute()
    graph.output.getData()
}