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

Scala中带继承的字段初始化和构造函数序列,scala,inheritance,Scala,Inheritance,假设我有一个类Graph和一个子类MyGraph,Graph有一个方法build,允许子类定义自己的构建过程 在MyGraph中,我声明了一个字段node,它应该是build方法中的init。因此,我声明它为空。但是,当我创建一个MyGraph实例时,执行序列首先转到MyGraph.build,然后转到var node:node=null,这将使节点保持为null 在从父级调用的方法中初始化此类字段的正确方法是什么 class Graph { def build:Unit = Unit

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

MyGraph
中,我声明了一个字段
node
,它应该是build方法中的init。因此,我声明它为空。但是,当我创建一个
MyGraph
实例时,执行序列首先转到
MyGraph.build
,然后转到
var node:node=null
,这将使节点保持为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) 
    }
}

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

如果您的字段不是一直定义的,或者在您的情况下,如果您只是希望字段等待初始化,并且在初始化之前字段没有被访问的任何风险,则应首选使用
选项[Node]
,您可以使用以下语法:

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

来源:

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

如果您的字段不是一直定义的,或者在您的情况下,如果您只是希望字段等待初始化,并且在初始化之前字段没有被访问的任何风险,则应首选使用
选项[Node]
,您可以使用以下语法:

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

来源:

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

根据目前的代码,我能想到的唯一解决方案是:

trait Graph

case class MyGraph(node: Node) extends Graph

val myGraph = MyGraph(new Node)
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()
}
但是,关于您的具体问题的更多细节可能会产生更合适的解决方案

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

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
方法,并使用构造函数来初始化值。我还在
Graph
中将输出节点初始化为
None
,在
linearregraph
中将输出节点初始化为
Some(add)

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

trait Graph

case class MyGraph(node: Node) extends Graph

val myGraph = MyGraph(new Node)
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()
}

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

根据目前的代码,我能想到的唯一解决方案是:

trait Graph

case class MyGraph(node: Node) extends Graph

val myGraph = MyGraph(new Node)
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()
}
但是,关于您的具体问题的更多细节可能会产生更合适的解决方案

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

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
方法,并使用构造函数来初始化值。我还在
Graph
中将输出节点初始化为
None
,在
linearregraph
中将输出节点初始化为
Some(add)

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

trait Graph

case class MyGraph(node: Node) extends Graph

val myGraph = MyGraph(new Node)
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()
}

如果您能提供更多关于您的特定用例的详细信息,我可以帮助您完成一些任务refactoring@acidghost当然我已经添加了一个关于我正在处理的问题的更详细的解释。如果您能提供关于您的特定用例的更多细节,我可以帮助您完成一些任务refactoring@acidghost当然对于我正在处理的问题,我已经添加了更详细的解释。谢谢您的详细解释。使用构造函数而不是构建方法的一个问题是:这将使所有中间变量(如
mul
add
)最终成为类变量,我不想保留这些变量。如果我构建一个大型图,将会有大量这样的变量。我宁愿把它们作为局部变量。你对如何优雅地做到这一点有什么建议吗?我更新了我的答案。你可以在块中移动你想成为本地变量的变量
{}
注意/注意,在我发布的代码的第二个版本中,
output
被定义为一种方法,因此每次使用
output
时都会有一个方法调用,并重新计算输出值。现在这真是一个不错的解决方案!谢谢谢谢你的详细解释。使用构造函数而不是构建方法的一个问题是:这将使所有中间变量(如
mul
add
)最终成为类变量,我不想保留这些变量。如果我构建一个大型图,将会有大量这样的变量。我宁愿把它们作为局部变量。你对如何优雅地做到这一点有什么建议吗?我更新了我的答案。你可以在块中移动你想成为本地变量的变量
{}
注意/注意,在我发布的代码的第二个版本中,
output
被定义为一种方法,因此每次使用
output
时都会有一个方法调用,并重新计算输出值。现在这真是一个不错的解决方案!谢谢这太酷了!我以前不知道你能在scala做到这一点。谢谢这太酷了!我以前不知道你能在scala做到这一点。谢谢