private[this] Vs giving parameters without val/var in scala - scala

I understand that marking a val/var as private[this] inside a class and providing a class parameter without val/var is same.
class ABC (x:Int){..}
class ABC {
private[this] x:Int = 100
}
In both the cases, x can be accessed by only the constructors and the methods of the same object. Is my understanding correct ?
Thanks!

Both
import scala.reflect.runtime.universe._
reify{
class ABC(x: Int)
}.tree
and
reify {
class ABC(private[this] val x: Int)
}.tree
produce
{
class ABC extends AnyRef {
<paramaccessor> private[this] val x: Int = _;
def <init>(x: Int) = {
super.<init>();
()
}
};
()
}
And
reify{
class ABC {
private[this] val x: Int = 100
}
}.tree
produces
{
class ABC extends AnyRef {
def <init>() = {
super.<init>();
()
};
private[this] val x: Int = 100
};
()
}
So the answer to your question
In both the cases, x can be accessed by only the constructors and the methods of the same object.
is positive.
You can check that class ABC(x: Int) does create a (private[this]) field verifying that you can access it with this
class ABC(x: Int) {
this.x
}
But it's important to emphasize that Scala specification doesn't specify either that x in class ABC(x: Int) is a field or that it's not a field. This is just current behavior of Scalac and Dotty compilers.
Get the property of parent class in case classes
Disambiguate constructor parameter with same name as class field of superclass
Do scala constructor parameters default to private val?
Scala Constructor Parameters
I understand that marking a val/var as private[this] inside a class and providing a class parameter without val/var is same.
Well, it's not the same because with class ABC(x: Int) you have a constructor accepting Int.

Related

Disambiguate constructor parameter with same name as class field of superclass

When playing around with Scala I had a code like this:
class Superclass(var i : Int){}
class Subclass(i : Int) extends Superclass(0) {
print(i)
}
I found out that print(i) prints the constructor parameter i of Subclass(i : Int)
Now, my question is: in a situation like this, how do I access the field i of Superclass?
Type ascription can up-cast the type of this which effectively disambiguates the two identifiers
class Subclass(i : Int) extends Superclass(0) {
print((this: Superclass).i)
print(i)
}
As a side-note, there also exists the following syntax that could be used in the case of method members (and which perhaps is not well-known)
super[ClassQualifier]
For example, consider the following
trait A {
def f = "A"
}
trait B extends A {
override def f = "B"
}
class C extends A with B {
println(super[A].f)
println(super[B].f)
println(f)
override def f = "C"
}
new C
// A
// B
// C
#MarioGalic answered this question. I'll just make some additions that are too long for comments.
Common misunderstanding is that i in
class MyClass(i : Int)
is just a constructor parameter and not a field. Actually if we do
import scala.reflect.runtime.universe._
println(reify{
class MyClass(i : Int)
}.tree)
we'll see (Scala 2.13.2)
{
class MyClass extends AnyRef {
<paramaccessor> private[this] val i: Int = _;
def <init>(i: Int) = {
super.<init>();
()
}
};
()
}
So a parameter of primary constructor without val/var generates private[this] field. So
class MyClass(i : Int)
is similar to
class MyClass(private[this] val i : Int)
and NOT similar to Java's
public class MyClass {
public MyClass(int i) {
}
}
without fields.
We can check that i is a field referring to it with this inside class body
class MyClass(i : Int) {
println(this.i)
}
new MyClass(1) // prints 1
The field i is private[this] so we can't refer it outside the class body (or inside the body on instance different than this)
class MyClass(i : Int) {
//println(new MyClass(2).i) //doesn't compile
}
//new MyClass(1).i //doesn't compile
I didn't find proper place in Scala specification but such behavior is well-known for a long time. For example in "Scala for the impatient" by Cay S. Horstmann it's written (edition 2, section 5.7):
Construction parameters can also be regular method parameters, without val or var. How these parameters are processed depends on their usage inside the class.
If a parameter without val or var is used inside at least one method, it becomes a field. For example,
class Person(name: String, age: Int) {
def description = name + " is " + age + " years old"
}
declares and initializes immutable fields name and age that are object-private. Such a field is the equivalent of a private[this] val field (see Section 5.4,“Object-Private Fields,” on page 56).
Otherwise, the parameter is not saved as a field. It’s just a regular parameter that can be accessed in the code of the primary constructor. (Strictly speaking, this is an implementation-specific optimization.)
Actually in 2.13.2 I can't confirm the second case.
Now let's have two classes.
Scala doesn't allow
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
//val i: Int = 2 //doesn't compile
}
unless we add override
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
override val i: Int = 2
}
But if Superclass's field is private[this] everything is ok without override
class Superclass {
private[this] val i: Int = 1
}
class Subclass extends Superclass {
val i: Int = 2
}
Actually if we try to add override this will not compile.
The reason is this being not overriding. One of fields is private[this] i.e. not accessible outside the object where the field is defined in, so these are just two different fields:
class Superclass {
private[this] val i: Int = 1
}
class Subclass extends Superclass {
val i: Int = 2
println(this.i) // or just println(i)
// println((this: Superclass).i) //doesn't compile
}
new Subclass
//2
or
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
private[this] val i: Int = 2
println(this.i) // or just println(i)
println((this: Superclass).i)
}
new Subclass
//2
//1
So in our case
class Superclass(var i : Int)
class Subclass(i : Int) extends Superclass(0)
are like
class Superclass extends AnyRef {
var i: Int = _
def this(_i: Int) = {
super() //pseudocode
i = _i
}
}
class Subclass extends Superclass {
private[this] val i: Int = _ //pseudocode
def this(_i: Int) = {
super(0) //pseudocode
i = _i //pseudocode because "i" is a val -- well, compiler can do things that we can't do in source code
}
}
Inside Subclass this.i or just i refers to Subclass's field private[this] val i: Int and (this: Superclass).i refers to Superclass's field var i: Int.
Do scala constructor parameters default to private val?
Scala Constructor Parameters
https://www.scala-lang.org/old/node/8384.html

How does Scala handle Any to AnyVal covariance in method return types?

An example is worth a thousand words:
class A { def foo: Any = new Object }
class B extends A {
override def foo: AnyVal = 42
}
In Java, the signature #Override public int foo() wouldn't even be allowed, and the overridden method foo in B could only return the wrapper integer type (#Override java.lang.Integer foo()).
Is Scala able to avoid the boxing/unboxing of AnyVal values in the overridden def foo: AnyVal method above?
No, it does not. Scala has to adhere to emitting the correct bytecode:
λ scalac -Xprint:jvm Bar.scala
[[syntax trees at end of jvm]] // Bar.scala
package yuval.tests {
class A extends Object {
def foo(): Object = new Object();
def <init>(): yuval.tests.A = {
A.super.<init>();
()
}
};
class B extends yuval.tests.A {
override def foo(): Object = scala.Int.box(42);
def <init>(): yuval.tests.B = {
B.super.<init>();
()
}
}
}
You can see that although AnyVal was permitted in Scala, the actual method signature for the emitted foo is Object and not AnyVal, and Int is boxed.
Yuval's answer can be generalized: erasure of AnyVal is Object (you can see this e.g. by entering classOf[AnyVal] in the REPL), so whenever you have AnyVal in Scala, you can expect Object in bytecode.
E.g. if you change A to
class A { def foo: AnyVal = 0 }
it's still Object.
Maybe there is some situation in which using AnyVal itself will avoid boxing, but I would be surprised. It was created pretty much for compiler's convenience, and picked up another use (value classes) later, but it's rarely useful in user code (except for defining value classes).

Trait vs Abstract Class in Scala

Assuming no multiple inheritance and no concerns about interoperability with Java, are the two following declarations equal?
sealed trait Foo { def x: Int }
case object Bar extends Foo { val x = 5 }
and
sealed abstract class Foo(val x: Int)
case object Bar extends Foo(5)
First, some modifications to your code (see below). I dropped the case as it is not relevant here. I also added val in the constructor of Foo2 as x is otherwise not accessible in Bar2.
sealed trait Foo { def x: Int }
object Bar extends Foo { val x = 5 }
sealed abstract class Foo2(val x: Int)
object Bar2 extends Foo2(5)
object Main {
def main(args: Array[String]) : Unit = {
println( Bar.x )
println( Bar2.x )
}
}
Are the two following declarations equal?
We need to define, what equal means:
Equal wrt. general structure: No. A trait is not an abstract class. A trait can have no constructor parameters, while a class can. On the other hand, a class or object (here Bar2) can only derive from one (abstract) class, while it could mix-in multiple traits. A good comprehesion on traits vs. abstract class is given here: http://www.artima.com/pins1ed/traits.html#12.7 if you need to decide on a trait or a class.
Equal wrt. byte code: No. Just run javap -v <classfile> to convince yourself
Equal wrt. Bar and Bar2 only: Yes. Both can be accessed and used identically. Both are singletons and they expose a member variable x that is readable (but not writable) from outside.
Also the output of scalac -print is quite helpful, to see what is going on:
sealed abstract trait Foo extends Object {
def x(): Int
};
object Bar extends Object with com.Foo {
private[this] val x: Int = _;
<stable> <accessor> def x(): Int = Bar.this.x;
def <init>(): com.Bar.type = {
Bar.super.<init>();
Bar.this.x = 5;
()
}
};
sealed abstract class Foo2 extends Object {
<paramaccessor> private[this] val x: Int = _;
<stable> <accessor> <paramaccessor> def x(): Int = Foo2.this.x;
def <init>(x: Int): com.Foo2 = {
Foo2.this.x = x;
Foo2.super.<init>();
()
}
};
object Bar2 extends com.Foo2 {
def <init>(): com.Bar2.type = {
Bar2.super.<init>(5);
()
}
};
object Main extends Object {
def main(args: Array[String]): Unit = {
scala.this.Predef.println(scala.Int.box(Bar.x()));
scala.this.Predef.println(scala.Int.box(Bar2.x()))
};
def <init>(): com.Main.type = {
Main.super.<init>();
()
}
}
No, these are not equivalent and can lead to trouble later with initialization order. I've added exactly the same line of code to each, showing they were never equal code blocks.
sealed trait Foo {
def x: Int
val calc = x * 5 / 2 //at the time this runs, x is actually 0
}
case object Bar extends Foo {
val x = 5
}
sealed abstract class Foo2(x: Int){
val calc = x * 5 / 2
}
case object Bar2 extends Foo2(5)
println(Bar.calc)
println(Bar2.calc)
//output 0 12

Why am I able to use the parameters passed to the class definition?

I'm just learning about classes and objects and Scala, and yesterday I saw something like this:
class Foo(bar: Int) {
def increaseByOne = bar + 1
}
Why am I able to use bar in method increaseByOne? I would expect the the method definition complain about not knowing bar.
I though the right way to define such a class would be
class Foo(x: Int) {
val bar = x
def increaseByOne = bar + 1
}
That's one of the wonderful features of Scala: if you reference constructor argument from any method that is not a constructor, Scala will automatically assign that constructor variable to a field. So effectively Scala translates your first code snippet into the second one for you (with private[this] modifier).
Moreover, preceding constructor argument with val/var will create getters/setters as well:
class Foo(val bar: Int)
val foo = new Foo(42);
println(foo.bar)
In this case bar is defined as private[this] and can be acessed within the class definition. You can check it with -Xprint:typer option:
class Foo extends java.lang.Object with ScalaObject {
<paramaccessor> private[this] val bar: Int = _;
def this(bar: Int): $line1.$read.$iw.$iw.Foo = {
Foo.super.this();
()
}
}

Duplicate methods due to type erasure in spite of #specialized

Stumbled over that
def foo(f: Int => Unit) {}
def foo(f: Long => Unit) {}
doesn't compile because of method foo is defined twice. I know that above is only a shorthand for
def foo(f: Function1[Int, Unit]) {}
def foo(f: Function1[Long, Unit]) {}
and that after type erasure both methods have same signature.
Now I've read in Try out specialized Function1/Function2 in 2.8.0 RC1! that Function1 and Function2 have #specialized versions for Int, Long and Double since Scala 2.8. That surely means that Function[Int, Unit] and Function[Long, Unit] have separate class files at JVM level.
Would then not both signatures are different?
Is the problem, that second type parameter will continue to be erased? But same problem with
class Bar[#specialized T]
def foo(f: Bar[Int]) {}
def foo(f: Bar[Long]) {}
it doesn't compile.
#specialized has nothing to do with type erasure, at least in this case. It means that an extra version of your class is generated with the native type in the position. This saves on boxing/unboxing notably.
So you define a class like:
class MyClass[#specialized(Int) T] {
def foobar(t: T) = {}
}
and you get two classes as output, (approximately):
class Foobar[java.lang.Object] {
def foobar(t: java.lang.Object) = {}
}
class Foobar[int] {
def foobar(t: int) = {}
}
You need to have two implementations of the class because you can't always guarantee that the one with the correct native type will be called. The scala compiler will choose which one to call. Note that the java compiler has no idea this specialization is taking place, so must call the unspecialized methods.
In fact, the output is the following (via JAD):
public class MyClass implements ScalaObject {
public void foobar(Object obj) { }
public void foobar$mcI$sp(int t) {
foobar(BoxesRunTime.boxToInteger(t));
}
public MyClass() { }
}
public class MyClass$mcI$sp extends MyClass {
public void foobar(int t) {
foobar$mcI$sp(t);
}
public void foobar$mcI$sp(int i) { }
public volatile void foobar(Object t) {
foobar(BoxesRunTime.unboxToInt(t));
}
public MyClass$mcI$sp() {}
}
So your type erasure problem will not be fixed with #specialized.
Both for compatibility and for cases where the type parameters of Function1 are unknown, a method with the signature as if Function1 was not specialized must also be generated.
Inspired especially by Matthew Farwell's answer I've tried the following
class Bar[#specialized(Int) T](val t: T)
class Foo {
def foo(b: Bar[_]) { print(b.t) }
}
val bari = new Bar(1)
print(bari.t)
foo(bari)
with scalac -print and got:
// unspecialized version Bar[_] = Bar[Object]
class Bar extends Object with ScalaObject {
protected[this] val t: Object = _;
def t(): Object = Bar.this.t;
def t$mcI$sp(): Int = Int.unbox(Bar.this.t());
def specInstance$(): Boolean = false;
def this(t: Object): Bar = {
Bar.this.t = t;
Bar.super.this();
()
}
};
// specialized version Bar[Int]
class Bar$mcI$sp extends Bar {
protected[this] val t$mcI$sp: Int = _;
// inside of a specialized class methods are specialized,
// so the `val t` accessor is compiled twice:
def t$mcI$sp(): Int = Bar$mcI$sp.this.t$mcI$sp;
override def t(): Int = Bar$mcI$sp.this.t$mcI$sp();
def specInstance$(): Boolean = true;
override def t(): Object = Int.box(Bar$mcI$sp.this.t());
def this(t$mcI$sp: Int): Bar$mcI$sp = {
Bar$mcI$sp.this.t$mcI$sp = t$mcI$sp;
Bar$mcI$sp.super.this(null);
()
}
}
class Foo extends Object with ScalaObject {
// scalac compiles only ONE foo method not one for every special case
def foo(b: Bar): Unit = Predef.print(b.t());
def this(): Foo = {
Foo.super.this();
()
}
};
val bari: or.gate.Bar = new or.gate.Bar$mcI$sp(1);
// specialized version of `val t` accessor is used:
Predef.print(scala.Int.box(bari.t$mcI$sp()));
Foo.this.foo(bari)
But through foo only the unspecialized version of val t accessor is used, even for the specialized instance bari and indirectly bari's overridden method def t(): Object = Int.box(Bar$mcI$sp.this.t()); is called.