在 Scala 中使用选项的惯用方法
我正在将一些 Java 代码转换为 Scala,试图使代码尽可能地惯用。
所以,我现在有一些使用选项而不是可为空值的代码,我想知道事情是否是 Scalaiish,或者我是否错了。那么,你们能批评一下下面的代码片段吗?
我特别寻求反馈的领域是:
- 使用伴生对象作为工厂,根据我们是否要传递选项或字符串提供 2 个选项: String 构造函数好吗,或者我们应该始终暴露这样的事实:它是一个选项吗?
- 前提条件的使用:是否有更好的方法来断言 alpha3Code 和 name 是强制性的,并且必须为 alpha2Code 传递非空选项? (我求助于 Guava 来获取字符串实用程序,因为我还没有找到Scala API 中的任何内容)
- hashCode、equals 和 toString 的实现。 equals 和 toString 再次委托给 Guava,而 equals 使用模式匹配。有没有更 Scala 风格的方式?
- 我知道我可以使用案例类,这会创建默认实现,但我最感兴趣的是学习如何在无法使用案例类的情况下实现这些实现。
package com.sirika.openplacesearch.api.language
import com.google.common.base.Objects
import com.google.common.base.Strings
object Language {
def apply(name : String, alpha3Code : String, alpha2Code : Option[String]) = new Language(name, alpha3Code, alpha2Code)
def apply(name : String, alpha3Code : String, alpha2Code : String = null) = new Language(name, alpha3Code, Option(alpha2Code))
def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code )
}
class Language(val name : String, val alpha3Code : String, val alpha2Code : Option[String]) {
require(!Strings.isNullOrEmpty(alpha3Code))
require(!Strings.isNullOrEmpty(name))
require(alpha2Code != null)
override def hashCode(): Int = Objects.hashCode(alpha3Code)
override def equals(other: Any): Boolean = other match {
case that: Language => this.alpha3Code == that.alpha3Code
case _ => false
}
override def toString() : String = Objects.toStringHelper(this)
.add("name", name)
.add("alpha3", alpha3Code)
.add("alpha2", alpha2Code)
.toString()
}
I am converting some Java code to Scala, trying to make the code as idiomatic as possible.
So, I now have some code using Options instead of nullable values, and I wonder whether things are Scalaiish, or whether I'm wrong. So, could you guys please criticize the following snippet of code?
The areas in which I am specifically looking for feedback are:
- The use of a companion object as a factory, giving 2 options depending on whether we want to pass Options or Strings: is the String constructor fine, or should we always expose the fact that it is an Option?
- The use of preconditions: are there better ways to assert the fact that alpha3Code and name are mandatory, and a non-null option mustbe passed for alpha2Code? (I am resorting to Guava for the string utils, as I haven't found anything in the Scala API)
- The implementation of hashCode, equals and toString. equals and toString delegate to Guava again, whereas equals uses pattern matching. Is there a more Scala-ish way?
- I know I could have used Case classes, which would have created default implementations, but I am mostly interested in learning how I should implement those for the cases where case classes cannot be used.
package com.sirika.openplacesearch.api.language
import com.google.common.base.Objects
import com.google.common.base.Strings
object Language {
def apply(name : String, alpha3Code : String, alpha2Code : Option[String]) = new Language(name, alpha3Code, alpha2Code)
def apply(name : String, alpha3Code : String, alpha2Code : String = null) = new Language(name, alpha3Code, Option(alpha2Code))
def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code )
}
class Language(val name : String, val alpha3Code : String, val alpha2Code : Option[String]) {
require(!Strings.isNullOrEmpty(alpha3Code))
require(!Strings.isNullOrEmpty(name))
require(alpha2Code != null)
override def hashCode(): Int = Objects.hashCode(alpha3Code)
override def equals(other: Any): Boolean = other match {
case that: Language => this.alpha3Code == that.alpha3Code
case _ => false
}
override def toString() : String = Objects.toStringHelper(this)
.add("name", name)
.add("alpha3", alpha3Code)
.add("alpha2", alpha2Code)
.toString()
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为你应该在工厂方法中只公开
Option[String]
。例如,作为您库的用户,我也会问自己应该使用哪种工厂方法的问题。我很可能会使用 Option。Scala 为我们提供了足够的工具来让我们的生活更轻松。例如,您可以使用默认选项,如下所示:
如果我再次作为库的用户,只想传递字符串而不每次将其包装在
Some
中,我可以像这样编写自己的隐式转换:甚至(如果我个人使用 nulls):
但我相信,如果你强制执行选项,它会让你的 API 更加坚实和清晰。
I think you should expose only
Option[String]
in factory method. For example I, as a user of your library, will also ask myself question which factory method I should use. And most probably I will use Option.Scala gives us enough tools to make our lifes easier. For example you can use default for option like this:
If I, again as user of your library, want to pass just string without wrapping it in
Some
each time, I can write my own implicit conversion like this:or even (if I personally use nulls):
But I believe, if you enforce option, it will make your API more solid and clear.
您应该避免使用
null
,除非有充分的理由不这样做。事实上,你可以这样写:前提条件很好。你可以这样写:
不过,不一定是改进。
String
有hashCode
,所以我不明白为什么你要调用另一个方法来生成哈希码,而不是仅仅调用alpha3Code.hashCode
。不过,我确实认为 Scala API 中有一些东西。没有把握。equals
代码应该有一个canEqual
方法,除非您将类设为sealed
或final
。模式匹配几乎就是实现它的方法,尽管考虑到提取器的存在,您可以像这样编写它:但是您编写它的方式几乎是通常编写的方式。
You should avoid
null
unless there's a very good reason not to. As it is, you could have just written this:The preconditions are fine. You could write it like this:
Not necessarily an improvement, though.
A
String
hashashCode
, so I don't understand why you are calling another method to generate a hash code instead of just callingalpha3Code.hashCode
. I do think there's something in the Scala API, though. Not sure.The
equals
code should have acanEqual
method, unless you make your classsealed
orfinal
. Pattern match is pretty much the way to do it, though you could have written it like this given the presence of an extractor:But the way you wrote it is pretty much the way it is usually written.
我不喜欢选项——它们增加了一定程度的间接性,在许多情况下这是不必要的和令人困惑的。我更不喜欢空值,所以我知道使用选项通常是合理的。但是,您应该始终查看是否有更自然的方法来消除界面中
Option
的使用。默认参数或单独的重载通常是更好的选择。所以我会像这样重写你的代码:
番石榴文档
I dislike Options -- they add a level of indirection that's unnecessary and confusing in many cases. I dislike nulls even more, so I understand that often the use of Options is justified. However, you should always see if there is a more natural way to eliminate the the use of
Option
in an interface.Default parameters or separate overloads are often a better option. So I'd rewrite your code like this:
Guava docs