I have following code snippet:
class Data(i: Int)
class Person(#transient val data: Data) extends java.io.Serializable
class Student(data: Data) extends Person(data)
I thought data is a field of Student class, but actually, it is a method of Student,
classOf[Student].getMethods.foreach(m => println(m.getName()),
The above code prints data
I would ask why data becomes method of Student, but not field,
thanks!
In Scala classes all public fields are actually private fields accessed via public methods.
This can be demonstrated using just your Data class, examining its status after phase 4 ("typer") of compilation.
%%> cat so.sc
class Data(i: Int)
%%> scalac -Xprint:4 so.sc
[[syntax trees at end of typer]] // so.sc
package <empty> {
class Data extends scala.AnyRef {
<paramaccessor> private[this] val i: Int = _;
def <init>(i: Int): Data = {
Data.super.<init>();
()
}
}
}
As you can see, val i is private[this] and if you run classOf[Data].getFields.isEmpty it will return true.
Now let's add a public field or two to it.
%%> cat so.sc
class Data(val i: Int) {
val x = 'X'
}
%%> scalac -Xprint:4 so.sc
[[syntax trees at end of typer]] // so.sc
package <empty> {
class Data extends scala.AnyRef {
<paramaccessor> private[this] val i: Int = _;
<stable> <accessor> <paramaccessor> def i: Int = Data.this.i;
def <init>(i: Int): Data = {
Data.super.<init>();
()
};
private[this] val x: Char = 'X';
<stable> <accessor> def x: Char = Data.this.x
}
}
We see that both vals, i and x, are private[this], and classOf[Data].getFields.isEmpty will still return true, but now there are also methods, def i and def x, that are public and return the expected values of Data.this.i and Data.this.x respectively.
Person has public getter method called data.
scala> :javap -public Person
Compiled from "<console>"
public class $line3.$read$$iw$$iw$Person implements java.io.Serializable {
public $line3.$read$$iw$$iw$Data data();
public $line3.$read$$iw$$iw$Person($line3.$read$$iw$$iw$Data);
}
Because Student is a child of Person it inherits this method.
Student has data too, but without var or val it's just a local variable, which is visible only in the body of Student, so it's not a member.
You can read more at Tour of Scala: Classes
Related
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.
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
What methods are generated for Scala case classes?
I know that some methods are generated specifically for case classes:
equals
canEqual
What are the others?
Also, I see that I can call productArity() on any case class. How does this work? In other words, why the following code is valid?
case class CaseClass()
object CaseClass {
val cc = new CaseClass()
cc.productArity
}
A good way what methods are generated for a specific class in Scala is to use the javap command.
Find the .class file that was compiled by scalac and then run the javap -private command on it from your respective command line tool. This will show you the constructors, fields, and all methods for a class.
You can do this for your case class to see what kinds of things are automagically supplied by Scala.
Case classes mixin the Product trait which provides the productArity method. For case classes the productArity method will return the count of the parameter list supplied in the class definition.
Given Test.scala -
case class Test()
You can run scalac Test.scala -print to see exactly what's generated
[[syntax trees at end of cleanup]] // Test.scala
package com {
case class Test extends Object with Product with Serializable {
<synthetic> def copy(): com.Test = new com.Test();
override <synthetic> def productPrefix(): String = "Test";
<synthetic> def productArity(): Int = 0;
<synthetic> def productElement(x$1: Int): Object = {
case <synthetic> val x1: Int = x$1;
case4(){
matchEnd3(throw new IndexOutOfBoundsException(scala.Int.box(x$1).toString()))
};
matchEnd3(x: Object){
x
}
};
override <synthetic> def productIterator(): Iterator = runtime.this.ScalaRunTime.typedProductIterator(Test.this);
<synthetic> def canEqual(x$1: Object): Boolean = x$1.$isInstanceOf[com.Test]();
override <synthetic> def hashCode(): Int = ScalaRunTime.this._hashCode(Test.this);
override <synthetic> def toString(): String = ScalaRunTime.this._toString(Test.this);
override <synthetic> def equals(x$1: Object): Boolean = {
case <synthetic> val x1: Object = x$1;
case5(){
if (x1.$isInstanceOf[com.Test]())
matchEnd4(true)
else
case6()
};
case6(){
matchEnd4(false)
};
matchEnd4(x: Boolean){
x
}
}.&&(x$1.$asInstanceOf[com.Test]().canEqual(Test.this));
def <init>(): com.Test = {
Test.super.<init>();
scala.Product$class./*Product$class*/$init$(Test.this);
()
}
};
<synthetic> object Test extends scala.runtime.AbstractFunction0 with Serializable {
final override <synthetic> def toString(): String = "Test";
case <synthetic> def apply(): com.Test = new com.Test();
case <synthetic> def unapply(x$0: com.Test): Boolean = if (x$0.==(null))
false
else
true;
<synthetic> private def readResolve(): Object = com.this.Test;
case <synthetic> <bridge> <artifact> def apply(): Object = Test.this.apply();
def <init>(): com.Test.type = {
Test.super.<init>();
()
}
}
}
It's true that a case classe automatically define equals and canEqual methods but it's also define getter methods for the constructor arguments. There's also a toString method that you can call.
A case class is also an instance of Product and thus inherit these methods. This is why you call productArity.
Considering this simple example:
trait A { def a: String = "a"; def a2: String }
case class C(b: String, c: String) extends A {
val a2 = "a2"
}
val c = C("b", "")
val copy = c.copy(c="c")
When I update field c with .copy(c="c"), are other fields(a,a2 and b) copied? Event though only their references are copied, if I have a huge hierarchy tree, will .copy become very costly?
Similarly:
class Foo {
val list = List(1,2,3,4,5,6)
}
val foo1 = new Foo
val foo2 = new Foo
Do foo1 and foo2 share an instance of List, or every time I instantiate a Foo it creates a new List? What if list is a var instead of val?
Generally Scala is immutable, you usually have to handle mutable cases yourself. Also case classes are immutable by nature and their copy method is generated by the compiler. So yes, they would share the same object reference. This is one of the reasons immutability is nice.
Your second question is a bit different. In that case the classes are constructed one after the other in seperate contexts.
It's also a good idea to check what's being compiled:
>scalac -print test.scala
[[syntax trees at end of cleanup]] // test.scala
package test {
class Foo extends Object {
private[this] val list: List = _;
<stable> <accessor> def list(): List = Foo.this.list;
def <init>(): b.Foo = {
Foo.super.<init>();
Foo.this.list = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6}));
()
}
}
}
From this we can see Scala creating a new list each time. Changing this to var won't change anything, we can check:
>scalac -print test.scala
[[syntax trees at end of cleanup]] // test.scala
package test {
class Foo extends Object {
private[this] var list: List = _;
<accessor> def list(): List = Foo.this.list;
<accessor> def list_=(x$1: List): Unit = Foo.this.list = x$1;
def <init>(): b.Foo = {
Foo.super.<init>();
Foo.this.list = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6}));
()
}
}
}
It only generated a setter method for it (def list_=(x$1: List)).
If you would like to reference the same list in both cases then initialize it with an object's default list:
object Foo {
val DefaultList = List(1,2,3,4,5,6)
}
class Foo {
var list = Foo.DefaultList
}
Which compiles to the following:
>scalac -print test.scala
[[syntax trees at end of cleanup]] // test.scala
package test {
object Foo extends Object {
private[this] val DefaultList: List = _;
<stable> <accessor> def DefaultList(): List = Foo.this.DefaultList;
def <init>(): test.Foo.type = {
Foo.super.<init>();
Foo.this.DefaultList = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6}));
()
}
};
class Foo extends Object {
private[this] var list: List = _;
<accessor> def list(): List = Foo.this.list;
<accessor> def list_=(x$1: List): Unit = Foo.this.list = x$1;
def <init>(): test.Foo = {
Foo.super.<init>();
Foo.this.list = Foo.DefaultList();
()
}
}
}
As you can see the list is only created once and then the reference through the def DefaultList(): List assigned on each Foo class instantiation.
To answer my own first question:
The concrete vals in a trait are implemented as a Java static method:
// scala
trait A { def a: String = "a"; }
// java
public static java.lang.String a(A);
Code:
0: ldc #8 // String a
2: areturn
Of course, a static method can't be copied. So I don't need to worry about huge hierarchy tree.
The abstract val is implemented as a hard-coded constant:
// scala
trait B { def b: String }
case class C() { def b = "b" }
// java
public java.lang.String b();
Code:
0: ldc #47 // String b
2: areturn
It's fine as well.
The only thing(s) will be copied when I call .copy() are the fields in constructor parameter list.
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.