【问题标题】:How to make fully functional immutable classes in Scala?如何在 Scala 中制作功能齐全的不可变类?
【发布时间】:2016-01-24 20:07:03
【问题描述】:

我注意到 Scala 有案例类。这些似乎是用于模式匹配,但我喜欢我可以用它们做到这一点:

val bankAccount1 = new BankAccount("Daniel", 100)
val bankAccount2 = bankAccount1.copy(funds = 200)

现在“丹尼尔”有两个银行账户,一个是 100 美元,一个是 200 美元。但是当事情变得更复杂时,BankAccount 需要被子类化,这不起作用,因为案例类不能扩展其他案例类。

我想要不可变的类,而不是可以扩展到更多不可变的类。就像我希望能够扩展 BankAccount 以拥有不可变的子类 SavingsBankAccountCheckingBankAccount。我不确定此时我是否需要扩展/实现Clonable 接口或定义自定义复制方法或类似的东西。我不想在课堂上放太多样板。

(如果可能),如何在 Scala 中创建可以复制和子类化且不会过于混乱或冗长的不可变类?

【问题讨论】:

  • 从功能性、不可变类继承的问题在于,它们不再(保证是)功能性、不可变的,因为子类可能会覆盖其中一种方法以产生副作用。尝试通过静态类型检查来解决这个问题很棘手,基本上相当于解决了停机问题。
  • 这就是我基本上解决这个问题的方法:youtube.com/edit?o=U&video_id=XeNptFcekSA。我做了一个隐式消息转发函数,并让子类有一个名为“parent”的参数转发给超类。超类本身只包含数据,然后我为功能添加了特征。这有多酷?

标签: java scala copy prototype


【解决方案1】:

我认为在 Scala 中这样做的惯用方式是使用封装而不是继承。例如,您可以:

case class BankAccount(id: AccountId, customer: Customer)
case class SavingsBankAccount(account: BankAccount, line: SavingsLine)

这样您将保持不变性和自动应用、取消应用、等于、hashCode 和复制方法生成的良好属性。

但是,如果您真的想使用继承,您别无选择,只能推出自己的自定义解决方案。例如,实现一个 trait SubclassableCaseClass,它实现了帮助方法,以便快速定义案例类免费提供给您的方法。

【讨论】:

  • 封装似乎是一个更好的解决方案。读写非常干净,不需要大规模重构或多余的样板。
  • Scala 是否有办法将帐户中的所有公共方法和变量代理:BankAccount 到“SavingsBankAccount”?就像我可以让 BankAccount 扩展代理或类似的东西吗?
  • 我明白了。我可以使用隐式关键字将 SavingsBankAccount.account 委托给 SavingsBankAccount 类。像这样:stackoverflow.com/questions/5477096/…
  • 是的,您可以使用隐式转换对SavingsBankAccount 进行操作,就好像它是BankAccount。但是尽量不要过度使用这种范式。尽管隐式转换非常有用且非常方便,但如果使用过多,它们可能会使代码难以理解,尤其是对于第一次学习代码库的新团队成员。
  • 这可能很愚蠢,但我最终将所有具有 Parent 子类的东西都设为了一个名为“Child”的通用抽象类,然后将这些 Child 类转发到 Child 中名为 Child.parent 的参数。特征可用于需要覆盖的方法。有点乱,但像这样:youtube.com/edit?o=U&video_id=XeNptFcekSA
【解决方案2】:

想想你的BankAccount 是否真的需要具体化。

我可以有一个不属于 Savings 或 Checking 类型的 BankAccount 吗?

如果这个问题的答案是否定的(在这个例子中我猜是),你可以使用抽象类或者...

特质

http://www.scala-lang.org/old/node/126

当你真的不需要层次结构时,你可以用特征做很多事情。实际上,特征比抽象类更好,因为您可以具有可组合性。

我发现在 scala 中进行编程,大多数时候您不需要扩展具体对象,特别是只打包数据的类。

如果你真的需要BankAccount 是具体的,我想你已经得到了你已经建议的内容。 :(

【讨论】:

  • 这是一个非常丑陋的解决方案,因为它不允许我在不更改具体对象的介绍特征然后重构所有内容的情况下处理已经存在的代码。
  • 更新了答案。抱歉,如果这不能解决您的问题:(。我从您的示例中看到,BankAccount 让我认为这可能是一个抽象类/特征。我不知道您的重构代码。但是相信我,在 Java 中我们习惯依赖很多具体对象的扩展,现在在scala中很少用了。
  • 只有一件事:抽象类不能解决您的问题(无需大量重构?)
  • 我不知道你在重构 :(,抱歉。但是在编写新代码时尝试遵循这一点。无法扩展案例类让我很恼火,但现在我很少需要它了。跨度>
猜你喜欢
  • 2017-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-18
  • 2020-12-11
  • 2019-11-05
  • 1970-01-01
相关资源
最近更新 更多