If a class inherits from a trait, and they both share a parent, it seems as if overriding an attribute of the parent will make it inaccessible from the trait. Namely
class TestClass(arg: String) extends Parent(arg) with TestTrait {
override val foo = "hey"
}
trait TestTrait extends Parent {
val bar = foo
}
abstract class Parent(arg: String) {
val foo = arg
}
Then running
val c = new TestClass("hello")
c.bar
will return null. This seems like counterintuitive behavior for me. Could anyone explain what the general inheritance rules and rationale behind them are to me?
EDIT: Thanks for the responses. However, I'm still confused as to why this works then:
class TestClass(arg: String) extends Parent(arg) with TestTrait {
// override val foo = "hey"
}
trait TestTrait extends Parent {
val bar = foo
}
abstract class Parent(arg: String) {
val foo = arg
}
Running the same as before will successfully produce "hello". Based on the explanations provided, I would've have expected null again. Sorry for not provided this context in the original phrasing.
Normally, val s are evaluated in sequence, meaning both in-type declaration sequence, and inheritance declaration sequence.
If there would be no override, bar would be evaluated based on the definition from the Parent constructor, since it is executed before the TestTrait constructor (because TestTrait is a subtype of Parent). So, bar would have whatever value it has in Parent.
However, since you override foo in TestClass, the evaluation takes place only once TestClass's constructor gets invoked, and this is after TestTrait.
The best rationale I could find for this in the SLS is within 5.3.1:
The signature and the self constructor invocation of a constructor
definition are type-checked and evaluated in the scope which is in
effect at the point of the enclosing class definition, augmented by
any type parameters of the enclosing class and by any early
definitions of the enclosing template. The rest of the constructor
expression is type-checked and evaluated as a function body in the
current class.
(emph. mine)
Implying the following pseudocode:
class TestClass(arg: String) extends Parent(arg) with TestTrait {
Parent(arg) //arg is ignored, since TestClass overrides foo.
TestTrait() //foo is still null at this point
override val foo = "hey"
}
Essentially boiling down to the same case as:
class A {
val a: String = b //a will be null, since b is not yet evaled
val b = "A"
}
(only without the luxury of a compiler warning)
In hindsight, this quirk is in fact hinted by Scala's language feature of early definitions. With your code, its use would look like the following:
class TestClass(arg: String) extends {
override val foo = "hey"
} with Parent(arg) with TestTrait
trait TestTrait extends Parent {
val bar = foo
}
abstract class Parent(arg: String) {
val foo = arg
}
scala> new TestClass("hello").bar
res0: String = hey
That's because constructors are executed top to bottom (from less derived type to most derived type).
Which means TestTrait is constructed before TestClass -> which means foo is null while TestTrait is being constructed.
Parent (arg = null, foo = null)
\
\
TestTrait (bar = null) Parent(arg = "hello", foo = "hello")
\ /
\ /
TestClass(arg = "hello", foo = "hey")
This may seem surprising, because dynamic dispatch works the other way around - methods are called on the most derived type first, and then on less derived types (if super.method() is invoked).
But there's a good reason for this: the initialization of a type often depends on the correct initialization of its base type.
Related
I read this statement:
By using User.getClass, you are referring to the class companion
object that Scala by default creates for the case class, and not the
case class itself.
To get the class object of the case class, use
classOf[User].
Where could I come unstuck by using the class of the companion object? I would have thought - showing my ignorance here - they would be the same.
Java has something called static methods
public class Foo {
private int a:
public Foo(a) { this.a = a; }
public int getA() { return a; }
static public String getB() { return "B"; }
}
it is like a method but not attached to an instance
var foo = new Foo(10);
foo.getA(); // method attached to foo, its value depends on foo
Foo.getB(); // method attached to class but not particular instance
These static methods are used for storing globals (not recommended), or stateless functions not depending on object - like e.g. factories and other utilities. These methods can access private/protected members of the instances, and instances can access private/protected static methods - so you can limit visibility as if they were inside the object, even though they aren't (Java's reflection treats their methods as if they had null as this).
In Scala we didn't want to have the distinction for static and non-static methods so we decided to do the following:
create a separate object where such methods could be stored
make sure that this object could access instances members and vice-versa
have a similar name to distinct this object from other objects
That's how we arrived at companion object.
class Foo(val a: Int)
object Foo {
val b = "B"
}
val foo = new Foo(10)
foo.a // this is normal method
foo.getClass // this would return Foo
class[Foo] // same as above
Foo.getClass // this would return Foo$
classOf[Foo.type] // same as above
getClass is method that you can call on any object - since foo and Foo have different classes they would return different values for getClass.
Any value can describe their type with .type so foo.type would be the type of foo variable (Foo) and Foo.type would be the type of Foo object which is companion of Foo class (and would be called Foo$ in bytecode).
From the reason why companion object exist, it follows that Foo and its companion does not have the same (instance) methods, so they cannot have the same interface, and so they cannot be of the same type.
When it comes to case classes they just automatically create a companion object (if it doesn't exist) and generate some methods inside it based on their constructor: e.g. apply, unapply.
Generally, class A and its companion object A define different (unrelated) static types, namely A and A.type
class A
object A
implicitly[A <:< A.type] // doesn't compile
implicitly[A.type <:< A] // doesn't compile
val a: A.type = new A // doesn't compile, we can't assign a class instance to an object variable
val a1: A = A // doesn't compile, we can't assign an object to a class variable
Similarly, they have different runtime classes (different representations in bytecode), namely A and A$
classOf[A] == Class.forName("A") // true
classOf[A.type] == Class.forName("A$") // true
classOf[A].isAssignableFrom(classOf[A.type]) // false
classOf[A.type].isAssignableFrom(classOf[A]) // false
Sometimes, when there is a reason, companions can agree in types (although such design decision is made quite rarely). Example: https://github.com/apache/spark/blob/v3.3.1/sql/catalyst/src/main/scala/org/apache/spark/sql/types/LongType.scala#L56
class A
object A extends A
// implicitly[A <:< A.type] // doesn't compile
implicitly[A.type <:< A] // compiles
classOf[A].isAssignableFrom(classOf[A.type]) // true
classOf[A.type].isAssignableFrom(classOf[A]) // false
// val a: A.type = new A // doesn't compile
val a1: A = A // compiles
or
trait B
class A extends B
object A extends B
val b: B = new A
val b1: B = A
Given:
class Foo(x: Int) {}
object Foo {
def apply(x: Int) = new Foo(x)
}
Besides marking Foo's constructor as private, how can I present a warning or compile-time failure when calling new Foo(...)?
In other words, I'd like to restrict (either by compile-time warning or error) construction of Foo to Foo.apply.
Is this possible?
In scala there are two idiomatic ways how to achieve that.
Constructor private to the class and companion object.
Factory has access to constructor, while anyone else doesn't:
class Foo private[Foo](val x: Int)
object Foo {
def apply(x:Int) = new Foo(x)
}
val foo = new Foo(1) // cannot compile
val foo1 = Foo(1) //compiles fine
Sealed abstract class.
In scala sealed class can be extended only in the same source file it is defined.
I suggest to make Foo sealed abstract class and return anonymous child of Foo in object's apply method.
sealed abstract class Foo(val x:Int)
object Foo {
def apply(x:Int):Foo = new Foo(x) {}
}
In this case Foo can be created nowhere except the file where it is defined.
UPD: Actually, this question was already discussed on stackoverflow.
UPD2: Added brief overview of both methods.
In the following code:
trait Base {
val foo: String = {
println("Hi, I'm initializing foo in trait Base")
"foo"
}
}
class Overrider extends Base {
override val foo = "bar!"
}
object Runner extends App {
println(new Overrider().foo)
println((new {override val foo = "baz"} with Base).foo)
}
Base trait's foo value initialisation is called regardless of whether I override the val by extending the trait or using an early initialiser:
Hi, I'm initializing foo in trait Base
bar!
Hi, I'm initializing foo in trait Base
baz
Is there a way to use vals and avoid that happening or should I just stick with lazy vals?
Either use lazy val as you mentioned or def. AFAIK there is no other way to avoid the initialization of vals in base classes. This is because everything outside class member definitions goes into the constructor. Therefore vals will be initialized on construction time.
Another approach would be to define an interface which you extend from:
trait Base {
def foo: String
}
class Foo extends Base {
override val foo = "foo"
}
class Bar extends Base {
override val foo = "bar"
}
As other users answered to your question, you have to define foo as a def if you do not want the Base trait method being valuated.
You told to me in the comments of your question that you were trying to implement a wiring module, as the one described in this link. Then, you're basically trying to implement the thin cake pattern.
In this case, it is not logically correct to declare foo as a val. foo represents a dependency that cannot be eagerly resolved . If you use a val, the two components will be tight coupled. You've to define foo as a def to let to your main application (or test) to wire foo to the correct type, i.e. a concrete class or a mock.
Let me know if you want some more explanations.
In Scala, it's possible to specify whether a function or class is covarient or contravarient in the following manner
class Foo[+arg] // covarient
class Bar[-arg] // contravarient
What are the real world uses of this feature?
I know the compiler runs checks to make sure that the stated entity is actually covarient or otherwise, but what is the benefit of even adding such annotations?
The simplest case where your probably already using it without knowing it is the scala collections.
class A()
class B() extends A
case class Container[T](elem : T)
val listOfA:List[A] = List[B](new B(),new B())
val containerOfA:Container[A] = Container[B](new B()) // fails
Imagine you have the following hierarchy:
class A
class B extends A
Covariance. Covariant type can be used as return type:
class Foo[+arg] { // Covariant
def getArg(): arg = ???
}
def testCovariant(): Unit = {
val fooB = new Foo[B]
val foo: Foo[A] = fooB
// returns only objects of class derived from A
// so it is safe
val a: A = foo.getArg()
}
So you can use any of Foo[DerivedClass]where Foo[BaseClass] is used, because anywhere Foo[BaseClass].getArg is called BaseClass is expected as result and any DerivedClass can be returned and assigned to it.
Contravariance. Contravariant type can be used as method parameter type:
class Bar[-arg] { // Contravariant
def setArg(p: arg): Unit = ???
}
def testContravariant(): Unit = {
val barA = new Bar[A]
val bar: Bar[B] = barA
// can set B to bar which is actually Bar[A]
// but Bar[A].setArg(p: A) can accept any object
// of type derived from A
// so it is safe
bar.setArg(new B)
}
Again. You can use any of Bar[DerivedClass] where Bar[BaseClass] is used, because anywhere Bar[DerivedClass].setArg(p: DerivedClass) is called DerivedClass is expected as argument and any Bar[BaseClass] can be used in this context, because you can always pass DerivedClass to Bar[BaseClass].setArg(p: BaseClass).
If I understand correctly, traits are the closest thing to Java interfaces and class constructors automatically set the variables.
But what if I have a class that extends a trait and has a constructor which sets a variable from the trait, so something like:
trait Foo {
var foo: String
}
class Bar (foo: String) extends Foo { /* ... */ }
Where I want the foo string of the trait been set when I make a Bar object.
The compiler seems to give me errors about this. What is the correct way to achieve this?
trait Foo { var foo: String = _ }
class Bar(foo0: String) extends Foo { foo = foo0 }
The trait declares an uninitialized var; the class then sets it equal to the input parameter.
Alternatively,
trait Foo {
def foo: String
def foo_=(s: String): Unit
}
class Bar(var foo: String) extends Foo {}
declares the getter/setter pair corresponding to a foo, which are set by the class.
Bar must define the abstract var foo in Foo (would be the same for a val). This can be done in the constructor
class Bar(var foo: String) extends Foo{...}
(of course, it could be done in the body of Bar too). By default, constructor parameters will be turned to private val if need be, that is if they are used outside the initiailization code, in methods. But you can force the behavior by marking them val or var, and possibly control the visibility as in
class X(protected val s: String, private var i: Int)
Here you need a public var to implement Foo.