scala - 处理基类与派生类字段名称的惯用 Scala 方式?

标签 scala field idioms

考虑 Scala 中的以下基类和派生类:

    abstract class Base( val x : String )

    final class Derived( x : String ) extends Base( "Base's " + x )
    {
        override def toString = x
    }

在这里,Derived 类参数的标识符“x”覆盖了 Base 类的字段,因此像这样调用 toString:
    println( new Derived( "string" ).toString )

返回派生值并给出结果“字符串”。

因此,对“x”参数的引用会提示编译器在 Derived 上自动生成一个字段,该字段在对 toString 的调用中提供。这通常非常方便,但会导致字段的复制(我现在将字段存储在 Base 和 Derived 上),这可能是不可取的。为了避免这种复制,我可以将 Derived 类参数从“x”重命名为其他名称,例如“_x”:
    abstract class Base( val x : String )

    final class Derived( _x : String ) extends Base( "Base's " + _x )
    {
        override def toString = x
    }

现在对 toString 的调用返回“Base 的字符串”,这就是我想要的。不幸的是,现在的代码看起来有些难看,并且使用命名参数来初始化类也变得不那么优雅了:
    new Derived( _x = "string" ) 

还有一种风险是忘记给派生类的初始化参数赋予不同的名称,并且无意中引用了错误的字段(这是不希望的,因为基类实际上可能持有不同的值)。

有没有更好的办法?

编辑 1 :澄清一下,我真的只想要基本值;派生的似乎只是初始化基类的字段所必需的。该示例仅引用它们来说明随后的问题。

编辑 2 :实际上,如果我使用 vars 而不是 vals,这个例子会更清楚,因为这突出了稍后在基类中更改值的问题:
    class Base( var x : Int ) { def increment() { x = x + 1 } }
    class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }

    val derived = new Derived( 1 )
    println( derived.toString )     // yields '1', as expected
    derived.increment()
    println( derived.toString )     // still '1', probably unexpected

编辑 3 :如果派生类最终会隐藏基类字段,那么有一种方法来抑制自动字段生成可能会很好。看起来 Scala 编译器实际上可以为您执行此操作,但这当然与“更近”标识符(派生类''x')隐藏更远程标识符(基类')的更一般规则相矛盾'X')。一个相当不错的解决方案似乎是像“noval”这样的修饰符,可能是这样的:
    class Base( var x : Int ) { def increment() { x = x + 1 } }
    class Derived( noval x : Int ) extends Base( x ) { override def toString = x.toString }

    val derived = new Derived( 1 )
    println( derived.toString )     // yields '1', as expected
    derived.increment()
    println( derived.toString )     // still '2', as expected

最佳答案

避免重复字段的惯用方法是编写

abstract class Base { val x: String }

final class Derived(val x: String) extends Base {
   def toString = x
}

但是,在您的版本中,看起来您实际上想要第二个字段,因为您有两个不同的值。正如您正确指出的那样,为这些字段提供相同的名称可能会导致混淆。

由于您实际上并不需要构造函数之外的构造函数参数,因此您可以使用这种方法(带有充当工厂的伴随模块的私有(private)构造函数):
abstract class Base { val x: String }

final class Derived private (val x: String) extends Base {
   def toString = x
}
object Derived {
   def apply(x: String) = new Derived("Base " + x)
}

关于scala - 处理基类与派生类字段名称的惯用 Scala 方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6515931/

相关文章:

c++ - 这种回调的使用是惯用的吗?

string - 在Scala中选择两个字符之间的子字符串

java - 有没有办法知道字段是否扩展 java.awt.Component?

c++ - 在 C++ 中,为什么我们不能使用 > 和 < 来比较迭代器?

dynamic - 有没有办法捕获未知的结构字段?

python - django:禁用某一特定字段的tinymce

c++ - 如何在创建时自动注册一个类

scala - 如何为具有单个可为空成员的案例类编写 Play JSON 写入转换器

scala - Scala 中 "reflective access of structural type member method should be enabled..."警告是什么意思?

scala - 执行上下文和调度程序 - 最佳实践、有用的配置和文档