成语Scala处理基类vs派生类字段名称的方法? [英] Idiomatic Scala way to deal with base vs derived class field names?

查看:116
本文介绍了成语Scala处理基类vs派生类字段名称的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Consider the following base and derived classes in Scala:

    abstract class Base( val x : String )

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

在这里,Derived类参数的标识符'x'覆盖了基类的字段,因此像这样调用toString:

Here, the identifier 'x' of the Derived class parameter overrides the field of the Base class, so invoking toString like this:

    println( new Derived( "string" ).toString )

返回Derived值并给出结果字符串".

returns the Derived value and gives the result "string".

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

So a reference to the 'x' parameter prompts the compiler to automatically generate a field on Derived, which is served up in the call to toString. This is very convenient usually, but leads to a replication of the field (I'm now storing the field on both Base and Derived), which may be undesirable. To avoid this replication, I can rename the Derived class parameter from 'x' to something else, like '_x':

    abstract class Base( val x : String )

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

现在对toString的调用返回"Base的字符串",这是我想要的.不幸的是,代码现在看起来有些丑陋,并且使用命名参数来初始化类也变得不太优雅:

Now a call to toString returns "Base's string", which is what I want. Unfortunately, the code now looks somewhat ugly, and using named parameters to initialize the class also becomes less elegant:

    new Derived( _x = "string" ) 

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

There is also a risk of forgetting to give the derived classes' initialization parameters different names and inadvertently referring to the wrong field (undesirable since the Base class might actually hold a different value).

有更好的方法吗?

编辑1 :为明确起见,我真的只希望使用Base值;派生域似乎只是初始化基类的字段所必需的.该示例仅引用它们以说明随后的问题.

Edit 1: To clarify, I really only want the Base values; the Derived ones just appear to be necessary for initializing the fields of the base class. The example only references them to illustrate the ensuing issues.

编辑2 :实际上,如果我使用vars而不是vals,该示例将更加清晰,因为这突出了稍后在基类中更改值的问题:

Edit 2: Actually, the example would have been clearer if I had used vars instead of vals, since that highlights the problem with values getting changed later on in the base class:

    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'这样的修饰符,也许是这样的:

Edit 3: It might be nice to have a way to suppress automatic field generation if the derived class would otherwise end up hiding a base class field. It would appear that the Scala compiler could actually have been designed to do this for you, but of course this contradicts the more general rule of "nearer" identifiers (the Derived class' 'x') hiding more remote ones (the Base class' 'x'). It seems like a reasonably nice solution would be a modifier like 'noval', maybe like this:

    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

推荐答案

避免重复填写字段的惯用方式是写

The idiomatic way to avoid duplicating the field would be to write

abstract class Base { val x: String }

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

但是,由于您有两个不同的值,因此在您的版本中,您似乎实际上需要第二个字段.正如您正确指出的那样,为这些字段指定相同的名称可能会导致混淆.

However, in your version it looks like you actually want a second field, since you have two distinct values. As you correctly point out, giving these fields the same name is likely to lead to confusion.

由于实际上不需要构造函数之外的构造函数参数,因此可以使用这种方法(带有构造为工厂的伴侣模块的私有构造函数):

Since you don't actually need the constructor argument outside of the constructor, you could use this approach (a private constructor with a companion module that acts as a factory):

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处理基类vs派生类字段名称的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆