【问题标题】:Method overriding, inheritance, and objects方法覆盖、继承和对象
【发布时间】:2019-04-27 00:28:17
【问题描述】:

请记住,我是在一周前开始使用 Scala 的。

: 货币 - 抽象、比特币、美元、欧元

性状: 转换器、打印机

我的抽象类有 2 个变量:NameValue(都是 vars)

Converter Trait 中的方法

def convert(to:Currency):Double = ???

这是我卡住的部分。我需要在我的每个子类(货币)中重写此方法,以便它们从一种货币转换为另两种货币。我可以用新方法做到这一点,但需要用一种方法来完成。我应该将什么作为参数传递,以便方法识别将其转换为哪个参数。 欧元、美元和比特币仅以一个 Int 作为参数。

我也不明白的是这个“to:”参数的一部分。

由于这也是我关于 Stack Overflow 的第一个问题,而且我不太确定这一切是如何工作的,我想指出我期待着指点和/或建议,因为简单的解决方案不会从长远来看,完全可以帮助我。如果你们中的一些人认为我遗漏了一些重要信息,请随时告诉我,我很乐意编辑我的问题。

【问题讨论】:

    标签: scala inheritance overriding abstract-class


    【解决方案1】:

    to: Currency 表示您正在声明一个名为to 的参数,其类型为Currency。不幸的是,您不需要 Currency 对象,因为它包含特定金额 - 您只想传递应该返回的货币类。

    好吧,你可以这样做:

    def convert(to: Class[Currency]): Double = ???
    

    但更类型安全的方法是这样做:

    def convert[C <: Currency](to: Class[C]): C = ???
    

    但我不确定您将如何实现该功能。您可能需要使用Manifest 而不是Class

    【讨论】:

      【解决方案2】:

      欢迎使用 *

      首先,作为对未来问题的建议,尽量提供所有(相关)代码 - 例如,在这种情况下,并发类的定义会很有用。

      其次,我相信这是一项任务,您可能不需要走得太远。
      正如 Robin 已经说过的,这个 to 参数用于确定目标类型 - 但是,因为它是一个类,所以它会有一个值,这是不需要的。
      我想这个想法是传递一个空类并使用模式匹配做这样的事情。

      class Bitcoin(override val amount: Int = 0) extends Concurrency(amount) {
        override def convert(to: Concurrency): Concurrency = to match {
          case _: Bitcoin => this // no need to transform
          case _: Euro    => new Euro((this.amount / 2).toInt)
          case _: Dollar  => new Dollar((this.amount / 3).toInt)
        }
      }
      
      new Bitcoin(30).convert(to = new Dollar()) // res0: Concurrency: Dollar(10)
      

      第三,这是一个类型更安全的解决方案,我希望你觉得它很有趣并且“有趣”(实用) - 故意开玩笑。

      sealed abstract class Concurrency(val amount: Int) {
        def name: String
      
        /** Tansforms this concurrency value to a new target type. */
        final def to[C <: Concurrency](implicit builder: Concurrency.Builder[C]): C =
          builder.build(this)
      }
      
      object Concurrency {
        /** Builder of target concurrencies. */
        trait Builder[C <: Concurrency] {
          def build(origin: Concurrency): C
        }
      }
      
      final case class Bitcoin(override val amount: Int) extends Concurrency(amount) {
        override final val name: String = "Bitcoin"
      }
      
      object Bitcoin {
        import Concurrency.Builder
      
        implicit val BitcoinBuilder: Builder[Bitcoin] = new Builder[Bitcoin] {
          override def build(origin: Concurrency): Bitcoin = origin match {
            case b: Bitcoin     => b // no need to transform
            case Euro(amount)   => Bitcoin(amount * 2)
            case Dollar(amount) => Bitcoin(amount * 3)
          }
        }
      }
      
      final case class Euro(override val amount: Int) extends Concurrency(amount) {
        override final val name: String = "Euro"
      }
      
      object Euro {
        import Concurrency.Builder
      
        implicit val EuroBuilder: Builder[Euro] = new Builder[Euro] {
          override def build(origin: Concurrency): Euro = origin match {
            case e: Euro         => e // no need to transform
            case Bitcoin(amount) => Euro((amount / 2).toInt)
            case Dollar(amount)  => Euro((amount / 1.5).toInt)
          }
        }
      }
      
      final case class Dollar(override val amount: Int) extends Concurrency(amount) {
        override final val name: String = "Dollar"
      }
      
      object Dollar {
        import Concurrency.Builder
      
        implicit val DollarBuilder: Builder[Dollar] = new Builder[Dollar] {
          override def build(origin: Concurrency): Dollar = origin match {
            case d: Dollar       => d // no need to transform
            case Euro(amount)    => Dollar((amount * 1.5).toInt)
            case Bitcoin(amount) => Dollar((amount / 3).toInt)
          }
        }
      }
      
      Dollar(10).to[Bitcoin] // res0: Bitcoin = Bitcoin(30)
      

      不要怀疑,要求澄清。

      【讨论】:

      • 非常感谢。我听从了你的建议(第一个)并解决了这个问题。将我的子类定义为案例类,并且几乎做到了。我还要感谢您为编写第二个解决方案付出的努力,这让我对如何正确编写 scala 有了一些了解。
      • @Borislav 不客气。我一直在想,也许你需要做的是修改输入类? - 因为在你的描述中你说value field 是一个var,返回类型是一个Double (我真的不明白为什么) 这是合理的,可能更简单。然而,这在 Scala 中不会是惯用的。