I generally have a pretty good feel for how Scala traits work, and how they can be used for different needs. As one example, a few years ago I learned that it’s best to define abstract fields in traits using def. But there are still a few things I wonder about.
Today I had a few free moments and I decided to look at what happens under the covers when you use def, val, and var fields in traits, and then mix-in or extend those traits with classes. So I created some examples, compiled them with scalac -Xprint:all, and then decompiled them with JAD to see what everything looks like under the covers.
I was initially going to write a summary here, but if you want to know how things work under the hood, I think it helps to work through the examples, so for today I’ll leave that as an exercise for the reader.
As a final note before jumping in, all of these tests were run with Scala 2.12.8 and Java 8.
def field in trait
 
 As a first example, this is how I defined a field as a def in a trait, and then implemented it in a class:
trait Foo {
    def id: Int
}
class Bar extends Foo {
    val id = 1
} 
 Next, this is what the final part of the output looks like when I compile that code with scalac -Xprint:all:
abstract trait Foo extends Object {
    def id(): Int
};
class Bar extends Object with Foo {
    private[this] val id: Int = _;
    <stable> <accessor> def id(): Int = Bar.this.id;
    def <init>(): Bar = {
        Bar.super.<init>();
        Bar.this.id = 1;
        ()
    }
} 
 And this is what I found when I decompiled the resulting class files with JAD:
public interface Foo {
    public abstract int id();
}
public class Bar implements Foo {
    private final int id = 1;
    public int id() {
        return id;
    }
    public Bar() {}
} 
 Results:
- A deffield in a trait results in an interface that has an abstract method with the same name and return type as the field
- The implementing class has a private, final backing field for the trait field, along with a corresponding “getter” method for that field
The part where the implementing class ends up with a private field corresponding to the def field was a surprise when I learned about it a few years ago, but it makes sense when you think the process through, i.e., how the code has to work on the JVM.
When I first started this process I thought it would be helpful to show the output from
scalac -Xprint:all, but to keep things simple here, I’ll only show the JAD output.
val field in trait (abstract)
 
 Next, here’s the source code for my abstract val example:
trait Foo {
    val id: Int   //abstract
}
class Bar extends Foo {
    val id = 1
} 
 Here’s the resulting JAD output:
public interface Foo {
    public abstract int id();
}
public class Bar implements Foo {
    private final int id = 1;
    public int id() {
        return id;
    }
    public Bar() {}
} 
 Results:
- An abstract valfield in a trait creates an interface with an abstract method with the same name and return type
- The implementing class has a private backing field for trait field, along with a corresponding “getter” method for that field
- At the class level, for this simple example, a deffield and an abstractvalfield seem to result in the same bytecode
I should note that there’s a slight difference in the output seen by scalac -Xprint:all for the two approaches. The final output from scalac for my def example looks like this:
abstract trait Foo extends Object {
    def id(): Int
}; 
 while the same final output for the val approach looks like this:
abstract trait Foo extends Object {
    <stable> <accessor> def id(): Int
}; 
 val field in trait (concrete)
 
 Here’s the source code for my concrete val field example:
trait Foo {
    val id = 0   //concrete
}
class Bar extends Foo {
    override val id = 1
} 
 Here’s the resulting JAD output:
public interface Foo {
    public abstract void $Foo$_setter_$id_$eq(int i);
    public abstract int id();
    public static void $init$(Foo $this) {
        $this.$Foo$_setter_$id_$eq(0);
    }
}
public class Bar implements Foo {
    private final int id = 1;
    public void $Foo$_setter_$id_$eq(int i) {}
    public int id() {
        return id;
    }
    public Bar() {
        Foo.$init$(this);
    }
} 
 Results:
- Because the field must be a concrete field in the trait, the resulting interface code is quite a bit more complex, including an initialization method
- It was a bit of a surprise for me to see what appear to be public “setter” methods in the resulting code, although the setter in the class doesn’t have a method body, and its idfield is marked final
var field in trait (abstract)
 
 Here’s the source code for my abstract var example:
trait Foo {
    var id: Int  //abstract
}
class Bar extends Foo {
    var id = 1   //works
} 
 Here’s the resulting JAD output:
public interface Foo {
    public abstract int id();
    public abstract void id_$eq(int i);
}
public class Bar implements Foo {
    private int id;
    public int id() {
        return id;
    }
    public void id_$eq(int x$1) {
        id = x$1;
    }
    public Bar() {
        id = 1;
    }
} 
 Results:
- varmeans “get and set,” so both getter and setter methods are generated
- As you would expect after the previous examples, the trait doesn’t have an idfield, and it has two abstract methods, a setter and a getter
- The class looks a little like a JavaBean, with a private field and getter/setter methods
var field in trait (concrete)
 
 Here’s the source code for my concrete var field example:
trait Foo {
    var id = 0
}
class Bar extends Foo {
    id = 1
} 
 Here’s the resulting JAD output:
public interface Foo {
    public abstract int id();
    public abstract void id_$eq(int i);
    public static void $init$(Foo $this) {
        $this.id_$eq(0);
    }
}
public class Bar implements Foo {
    private int id;
    public int id() {
        return id;
    }
    public void id_$eq(int x$1) {
        id = x$1;
    }
    public Bar() {
        Foo.$init$(this);
        id_$eq(1);
    }
} 
 Results:
- As usual, there’s no field in the interface
- There is an “init” method in the interface to set the field to 0initially
- The init method calls id_$eq, which updates the privateid; this is a different implementation than the “concreteval” example (which makes sense, because this is avarexample)
- Note that the Barconstructor makes two method calls; conversely, this is all it did in the “abstractvar” example:
public Bar() {
    id = 1;
} 
 An abstract class in the middle
To look at something a little more complicated, I created a trait T with a field t defined as a def. Then I extend that with an abstract class A, while also giving A a concrete field a. Then I create a concrete class B by extending A:
trait T {
    def t: Int
}
abstract class A extends T {
    val a = 1
    // class is abstract b/c it doesn’t implement `t`
}
class B extends A {
    val b = 2
    override val t = 3
} 
 Here’s the resulting JAD output:
public interface T {
    public abstract int t();
}
public abstract class A implements T {
    private final int a = 1;
    public int a() {
        return a;
    }
    public A() {}
}
public class B extends A {
    private final int b = 2;
    private final int t = 3;
    public int b() {
        return b;
    }
    public int t() {
        return t;
    }
    public B() {}
} 
 Results:
- The deffieldtin classTresults in an abstract method, as shown in my originaldefexample
- The class AimplementsT, but does not referencet
- The class Bimplementstas a private fieldtand a getter methodt(), which is also the same as my firstdefexample
- Putting an abstract class in the middle essentially had no effect on the relationship between the final class Band the traitT
A trait in the middle
As a final example, let’s see what happens if one trait extends another trait, they both have def fields, and a class extends the second trait:
trait T1 {
    def t1: Int
}
trait T2 extends T1 {
    def t2: Int
}
class C extends T2 {
    val t1 = 1
    val t2 = 2
} 
 If you’ve read through all of this so far, you probably have a decent feel for what the generated source code should look like. So before looking at the following code, I encourage you to work through this problem in your head and decide what you think the decompiled source code should look like for T1, T2, and the class C.
.
 .
 .
 .
 .
 .
Here’s the decompiled source code:
public interface T1 {
    public abstract int t1();
}
public interface T2 extends T1 {
    public abstract int t2();
}
public class C implements T2 {
    private final int t1 = 1;
    private final int t2 = 2;
    public int t1() {
        return t1;
    }
    public int t2() {
        return t2;
    }
    public C() {}
} 
 Results:
- As before, deffields in traits lead to abstract methods in the generated code
- As in the previous example, the trait in the middle knows nothing about the t1()method when it extends the traitT1
- As in previous examples, the final class has private fields corresponding to the fields defined in the trait(s) it extends, with public getter methods for those fields
Summary
As I wrote in the beginning, I didn’t want to include a summary here because I think it’s best to work through the examples. Therefore, I won’t add a summary here, but if you want to add your own summary in the Comments section below, feel free to do so.










