Do I need a constructor for the class definition?

scala> class Mytest {
     |   val foo = 0
     | }
class Mytest

scala> val t = new Mytest
val t: Mytest = Mytest@3514237f

as you see here, I didn’t specify a constructor, but the “new” does work.
I am not sure for what happened here.


All classes have a constructor. Otherwise, you wouldn’t be able to instantiate there.

In this case, as the code clearly shows, the constructor is an empty one.

What you probably aren’t used to is that Scala treats the class declaration itself as the ultimate constructor for the class. Unlike Java or most other OO languages, classes in Scala can take arguments. Here’s a minor alteration to your class as an example.

scala> class MyTest(bar: Int) {
     |   val foo = 0
     | }
// defined class MyTest
scala> val t = new MyTest(42)
val t: MyTest = MyTest@30159886

When we call new, we are effectively calling the class definition and giving it whatever arguments it asks for. At the same time, the “body” of the class is executed top to bottom. It is during that execution of the body that foo is created as a member with the value of 0.

Your original example didn’t have an argument list, so when you created an instance, nothing needed to be passed and the body just executed.


For convenience, you always get empty parens if you omit them. This is different from function args in Scala 3.

scala> class C
// defined class C

scala> new C()
val res0: C = C@41a16eb3

scala> class C()
// defined class C

scala> new C
val res1: C = C@5e69cf07

scala>  def f() = 42
def f(): Int

scala> f
-- [E100] Syntax Error: ------------------------------------------------------------------------------------------------
1 |f
  |method f must be called with () argument
  | longer explanation available when compiling with `-explain`
1 error found

In Scala 2, parens are provided when overriding, but not in Scala 3 apparently:

scala> class C { def f() = 42 }
// defined class C

scala> class D extends C { override def f = 27 }
-- [E163] Declaration Error: -------------------------------------------------------------------------------------------
1 |class D extends C { override def f = 27 }
  |                                 ^
  |                                 error overriding method f in class C of type (): Int;
  |                                   method f of type => Int has incompatible type
  | longer explanation available when compiling with `-explain`
1 error found

You still get a pass for Java-defined methods such as toString.

I wonder if “free parens for class definitions” would have been added to the language if “universal apply” had been conceived instead. Once you write C(), I think it is more natural to write class C(), although class C is svelte and attractive. In a word, “classy”.