How to implement enums with values in Scala 2.12.15 - scala

In Java, one can use (from https://www.baeldung.com/java-enum-values#adding-constructor)
public enum Element {
H("Hydrogen"),
HE("Helium"),
NE("Neon");
public final String label;
private Element(String label) {
this.label = label;
}
}
to construct Enums with string values, such that NE.label would yield "Neon".
How do you do the same thing, but in Scala version 2.12.15? (Please show the above example translated to Scala)

The best way to create enums in Scala is through a basic ADT like this:
sealed abstract class Element(val label: String)
object Element {
final case object H extends Element(label = "Hydrogen")
final case object HE extends Element(label = "Helium")
final case object NE extends Element(label = "Neon")
}
If you want some goodies, like getting the list of all elements or a getByName method, for free.
Then the best would be to combine the above with the Enumeratum library.

Related

Scala instantiate common val on two classes with same parent

I have two classes that extend the same trait :
trait Event {
val propertyCommon : String = ""
}
case class EventA(propertyA : String) extends Event
case class EventB(propertyB : String) extends Event
Now I instantiate my classes in a List :
myList : List[Event] = List(EventA("a"),EventB("b"))
What I want to do is to instantiate after the common property, as if there was a copy method in my trait :
myList.map(_.copy(propertyCommon = "Something"))
How could I do that ?
What you say you want to do is called a prototype pattern in OOP, and as far as I can tell it isn't supported out of the box in Scala (and I think anywhere else).
There are also a few problems:
propertyCommon is not set up using constructor, so each implementation that would have to set it, would most likely have to override it using anonymous class (but then, this property will not be a part of equals, hashcode, toString, derivation, etc)
Event doesn't define any interface that would allow updating it
The easiest (and safest) way I see to implement functionality you want, would be something like this:
trait Event {
val propertyCommon: String // will be set by implementing class
def clone(propertyCommon: String = this.propertyCommon): Event
}
// children define propertyCommon to match the interface
case class EventA(propertyA: String, propertyCommon: String = "") extends Event {
def clone(propertyCommon: String = this.propertyCommon): EventA = copy(propertyCommon)
}
case class EventB(propertyB: String, propertyCommon: String = "") extends Event {
def clone(propertyCommon: String = this.propertyCommon): EventB = copy(propertyCommon)
}
However, probably if we knew more about your problem we could provide some other, simpler solution.

Converting package object contents from scala to java

I have the unfortunate task of converting a networking library that I wrote couple years back from scala to java due to lack of offshore scala resources.
One of the tricker areas : converting the package object and its type aliases and case classes. Here is an excerpt:
package object xfer {
type RawData = Array[Byte]
type DataPtr = String
type PackedData = RawData
// type PackedData = (DataPtr, RawData, RawData)
// type UnpackedData = (DataPtr, Any, RawData)
type UnpackedData = Any
case class TaggedEntry(tag: String, data: Array[Byte])
case class TypedEntry[T](tag: String, t: T)
case class XferWriteParams(tag: String, config: XferConfig, data: RawData, md5: RawData) {
override def toString: DataPtr = s"XferWriteParams: config=$config datalen=${data.length}} md5len=${md5.length}}"
}
As an example the RawData has 32 usages. I suppose that one approach could be to do simple Find/Replace of all 32 instances with byte[]: but is there a more elegant way?
For the case class'es .. I'm leery of creating another few top level files in this package for each of them - and likewise another few top level files in each of a dozen other packages .. but is there any alternative?
ADT-esque trait-case-class clusters like
trait T
case class C1 extends T
case class C2 extends T
could be converted to an abstract base class T, with nested static classes C1, C2:
abstract class T {
static class C1 extends T { ... }
static class C2 extends T { ... }
}
This would at least eliminate the need to explode each such enumeration into thousand top-level classes in separate files.
For type-aliases, you might consider promoting them to full fledged wrapper classes, but you would have to be very careful whenever instantiating classes like RawData, using ==/equals, hashCode, or putting them in HashSets or HashMaps. Something like this might work:
class RawData {
private final byte[] value;
public RawData(byte[] v) { ... }
public byte[] getValue() { ... }
public static RawData of(byte[] value) {
return new RawData(value);
}
#Override public int hashCode() {
return value.hashCode();
}
#Override public boolean equals(Object other) {
if (other instanceof RawData) {
return value.equals(((RawData) other).value);
} else {
return false;
}
}
#Override public String toString() {
return String.valueOf(value);
}
}
This would keep the signatures similar, and might even enhance type-safety to some extent. In cases where it is really performance critical, I'd propose to just find/replace all occurrences by byte[].

ordinal() method in scala Enumeration

in java i've got enum say
enum Num {
ONE,
TWO,
THREE;
private static Num current = ONE;
private static Num next = TWO;
public Num getNext(){
return values()[(ordinal() + 1) % values().length];
}
public static Num getNextNum(){
next = current.getNext()
}
}
so i'm able to assign next in that fashion calling getNextNum(), but scala enums, as it seems to me, lacks this feature. do u know any other workaround except using java enum in scala code?
Take a look at Case objects vs Enumerations in Scala
In Scala, Enumeration has limited functionality and it's preferable to have sealed traits if you want to have functionality added to your Enumeration.
For example:
sealed trait Num {
val next: Num
}
case object ONE extends Num {
override val next = TWO
}
case object TWO extends Num {
override val next = THREE
}
case object THREE extends Num {
override lazy val next = ONE
}
Also in your case, it's a bit tricky since you want to refer to a value before it's initialized. So there is a lazy on the last definition (you can also define next as def, if you want to)
In case you wanted to achieve this behavior using standard Scala Enumeration, you can redefine Enumeration#Value as instances of your enum:
object Num extends Enumeration {
case class MyValue() extends Val {
def next: MyValue = Num((id + 1) % Num.values.size).asInstanceOf[MyValue]
}
val One, Two, Three = MyValue()
}
Num.One.next
// res0: Num.MyValue = Two
However, since Enumerations are bad due to so many reasons, as well as don't have consistent IDE support, I personally would use sealed traits with case objects for enums (check out enumeratum) which allow to introduce arbitrary logic for my enums due to precise control over what's going on.

Scala, how do I replace static members?

I wan't to build a class Animal which I can find the number of animals were created. In Scala there is no option to static variable, so how can I implement such functionality in Scala (I am looking for non-specific solution)?
Thanks!
For example in Java:
public class Amimal {
static int number_of_aminals = 0;
public Animal() {
number_of_animals++;
}
}
You can create a companion object for your case class which acts as a singleton:
case class Animal(name:String) {
Animal.incrementAnimal
}
object Animal {
def incrementAnimal = ...
}
However, be advised that following the approach above will require you to use mutable variables (variables defined by var instead of val) which is discouraged in Scala. So you may want to revisit your design to use immutable values.
One option would be:
import Animal
class Animal {
Animal.increment()
}
object Animal {
private[this] var _count = 0
def increment(): Unit = { _count += 1 }
def count: Int = _count
}
Though you might want to use AtomicInt.

Enums in Scala with multiple constructor parameters

I am writing my first large Scala program. In the Java equivalent, I have an enum that contains labels and tooltips for my UI controls:
public enum ControlText {
CANCEL_BUTTON("Cancel", "Cancel the changes and dismiss the dialog"),
OK_BUTTON("OK", "Save the changes and dismiss the dialog"),
// ...
;
private final String controlText;
private final String toolTipText;
ControlText(String controlText, String toolTipText) {
this.controlText = controlText;
this.toolTipText = toolTipText;
}
public String getControlText() { return controlText; }
public String getToolTipText() { return toolTipText; }
}
Never mind the wisdom of using enums for this. There are other places that I want to do similar things.
How can I do this in Scala using scala.Enumeration? The Enumeration.Value class takes only one String as a parameter. Do I need to subclass it?
Thanks.
You could do this which matches how enums are used:
sealed abstract class ControlTextBase
case class ControlText(controlText: String, toolTipText: String)
object OkButton extends ControlText("OK", "Save changes and dismiss")
object CancelButton extends ControlText("Cancel", "Bail!")
I'd like to propose the following workaround for the issue:
object ControlText extends Enumeration {
type ControlText = ControlTextValue
case class ControlTextValue(controlText: String, toolTipText: String) extends Val(controlText)
val CANCEL_BUTTON = ControlTextInternalValue("Cancel", "Cancel the changes and dismiss the dialog")
val OK_BUTTON = ControlTextInternalValue("OK", "Save the changes and dismiss the dialog")
protected final def ControlTextInternalValue(controlText: String, toolTipText: String): ControlTextValue = {
ControlTextValue(controlText, toolTipText)
}
}
Now you can use ControlText as Java enum:
val c: ControlText
c.toolTipText
The only a little bad smell is to get enum object by withName or apply methods. You have to do a cast:
val c: ControlText = ControlText.withName(name).asInstanceOf[ControlText]
Following on from Mitch's answer, if you find that the sealed behaviour is not restrictive enough in limiting subclassed instances to the file where the base class is defined, you can use an object (module) definition like this:
object ControlTexts {
sealed abstract class ControlTextBase
case class ControlText private[ControlTexts] (controlText: String,
toolTipText: String)
extends ControlTextBase
object OkButton extends ControlText("OK", "Save changes and dismiss")
object CancelButton extends ControlText("Cancel", "Bail!")
}
which obviously limits further instantiation of ControlText instances. The sealed keyword is still important in helping detect missing cases in pattern matching.