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:
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 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:
def apply(name: String, alpha3Code: String, alpha2Code: Option[String] = None) =
new Language(name, alpha3Code, alpha2Code)
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:
implicit def anyToOption[T](t: T): Option[T] = Some(t)
or even (if I personally use nulls):
implicit def anyToOption[T](t: T): Option[T] =
if (t == null) None else Some(t)
But I believe, if you enforce option, it will make your API more solid and clear.
You should avoid null unless there's a very good reason not to. As it is, you could have just written this:
def apply(name : String, alpha3Code : String, alpha2Code : String) = new Language(name, alpha3Code, Option(alpha2Code))
def apply(name : String, alpha3Code : String) = new Language(name, alpha3Code, None)
The preconditions are fine. You could write it like this:
require(Option(alpha3Code) exists (_.nonEmpty))
require(Option(name) exists (_.nonEmpty))
Not necessarily an improvement, though.
A String has hashCode, so I don't understand why you are calling another method to generate a hash code instead of just calling alpha3Code.hashCode. I do think there's something in the Scala API, though. Not sure.
The equals code should have a canEqual method, unless you make your class sealed or final. Pattern match is pretty much the way to do it, though you could have written it like this given the presence of an extractor:
case Language(_, `alpha3Code`, _) => true
But the way you wrote it is pretty much the way it is usually written.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With