假设我有一个类Graph
和一个子类MyGraph
,Graph
有一个方法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)
}
}
答案 0 :(得分:1)
你应该真的避免在Scala中使用Java的null
。
如果您的字段未定义,或者在您的情况下,如果您只是希望您的字段等待初始化并且该字段没有任何风险,则应该更喜欢使用Option[Node]
在初始化之前访问,您可以使用以下语法:
class MyGraph extends Graph {
var node: Node = _
override def build: Unit {
node = new Node
}
}
答案 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
中的Graph
和Some(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()
}