6

Is there a way to override method in Scala 3 enum just like in Java?

public enum Test {

    ONE {
        @Override
        public int calc() {
            return 1;
        }
    },
    TWO {
        @Override
        public int calc() {
            return 2;
        }
    };

    public abstract int calc();
}

I've tried something like this, but no result. Also haven't found anything about enum methods overriding in documentation.

enum Test {
  def calc(): Int ={
    0
  }
  case One
    override def calc(): Int ={
      1
    }
  case Two
    override def calc(): Int ={
      2
    }
}

Maybe there is another way to achieve similar functionality?

davidalayachew
  • 1,279
  • 1
  • 11
  • 22
Andrei Yusupau
  • 587
  • 1
  • 11
  • 30
  • 2
    I don't think so, although it could be a handy feature. I'd suggest simply using sealed traits and case classes for now. – user Apr 24 '21 at 19:59
  • 1
    No, it is not possible. If you want that functionality, Java and Kotlin can provide it, and there are longhand ways to create this functionality in Scala. But no optional anonymous classes are allowed in Scala enums. – davidalayachew Apr 24 '21 at 20:01
  • 1
    You can also take `calc` as a parameter to `Test`. – user Apr 24 '21 at 20:02
  • 2
    If your enums have a very complicated logic that you find overriding to be clearer than a match, then you probably shouldn't be using an enum in the first place. – Luis Miguel Mejía Suárez Apr 24 '21 at 21:32

3 Answers3

7

The enum is sealed, so it cannot be extended after the fact, so there is no reason to override anything. Just collect all cases in one place, and instead of multiple override-methods, write one single method which covers all the cases:

enum A:
  case X(x: Int)
  case Y(y: String)
  def foo: String = this match {
    case X(x) => s"X = ${x}"
    case Y(y) => y
  }

val x = new A.X(42)
val y = new A.Y("y")
println(x.foo) // X = 42
println(y.foo) // y
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Thanks for your answer, but would it still look good if there are many enum cases and every method is bigger than one line? – Andrei Yusupau Apr 24 '21 at 20:43
  • 2
    @AndreiYusupau I'd argue that "yes", because 1. One wouldn't have to duplicate the `override def calc(): Int ={`-signature for each case class. 2. `match` is the natural way to work with enums, that's how they are supposed to be used. There is no need to deviate from the standard `match-case` in the declaration of `enum` itself. – Andrey Tyukin Apr 24 '21 at 20:50
4

It seems what you want is currently not possible, but there are other ways to do it. You could try an old-school sealed trait with objects that override calc.

sealed trait Test:
  def calc: Int
object One extends Test:
  def calc = 1
object Two extends Test:
  def calc = 2

The function calc could also be made a parameter of Test, although I like this method less.

enum Test(calc: () => Int):
  case One extends Test(() => 1)
  case Two extends Test(() => 2)

Another way to do it would be through a single method and pattern matching, as gianluca aguzzi and Andrey Tyukin did, although an extension method is unnecessary.

If calc has to be a function, I would suggest the first approach, or pattern matching if you feel it suits you better. Sealed traits are also a good option if you want to override multiple methods, since you don't need to pattern match separately or lump a bunch of lambdas into a constructor call. If it's not a function, I feel the second would work best.

user
  • 7,435
  • 3
  • 14
  • 44
1

In scala 3, you can achieve a similar result combining enum with extension method:

enum Test {
  case One, Two
}

extension (test: Test)
  def calc() : Int = test match {
    case Test.One => 1
    case Test.Two => 2
  }

I hope I helped you :)

gianluca aguzzi
  • 1,734
  • 1
  • 10
  • 22
  • 1
    I don't see why an extension method is needed at all. The method `calc` could easily be part of `Test` if you're not overriding it anyway. – user Apr 24 '21 at 20:13