score:67
I'm not an expert, but here is my understanding:
Traits are compiled into an interface and corresponding class.
trait Foo {
def bar = { println("bar!") }
}
becomes the equivalent of...
public interface Foo {
public void bar();
}
public class Foo$class {
public static void bar(Foo self) { println("bar!"); }
}
Which leaves the question: How does the static bar method in Foo$class get called? This magic is done by the compiler in the class that the Foo trait is mixed into.
class Baz extends Foo
becomes something like...
public class Baz implements Foo {
public void bar() { Foo$class.bar(this); }
}
Class linearization just implements the appropriate version of the method (calling the static method in the Xxxx$class class) according to the linearization rules defined in the language specification.
score:2
A very good explanation of this is in:
The busy Java developer's guide to Scala: Of traits and behaviors - Traits in the JVM
Quote:
In this case, it [the compiler] drops the method implementations and field declarations defined in the trait into the class that implements the trait
score:2
In the context of Scala 12 and Java 8, you can see another explanation in commit 8020cd6:
Better inliner support for 2.12 trait encoding
Some changes to the trait encoding came late in the 2.12 cycle, and the inliner was not adapted to support it in the best possible way.
In 2.12.0 concrete trait methods are encoded as
interface T {
default int m() { return 1 }
static int m$(T $this) { <invokespecial $this.m()> }
}
class C implements T {
public int m() { return T.m$(this) }
}
If a trait method is selected for inlining, the 2.12.0 inliner would copy its body into the static super accessor
T.m$
, and from there into the mixin forwarderC.m
.This commit special-cases the inliner:
- We don't inline into static super accessors and mixin forwarders.
- Instead, when inlining an invocation of a mixin forwarder, the inliner also follows through the two forwarders and inlines the trait method body.
score:5
For the sake of discussion, let's look the following Scala example using multiple traits with both abstract and concrete methods:
trait A {
def foo(i: Int) = ???
def abstractBar(i: Int): Int
}
trait B {
def baz(i: Int) = ???
}
class C extends A with B {
override def abstractBar(i: Int) = ???
}
At the moment (i.e. as of Scala 2.11), a single trait is encoded as:
- an
interface
containing abstract declarations for all the trait's methods (both abstract and concrete) - an abstract static class containing static methods for all the trait's concrete methods, taking an extra parameter
$this
(in older versions of Scala, this class wasn't abstract, but it doesn't make sense to instantiate it) - at every point in the inheritance hierarchy where the trait is mixed in, synthetic forwarder methods for all the concrete methods in the trait that forward to the static methods of the static class
The primary advantage of this encoding is that a trait without concrete members (which is isomorphic to an interface) actually is compiled to an interface.
interface A {
int foo(int i);
int abstractBar(int i);
}
abstract class A$class {
static void $init$(A $this) {}
static int foo(A $this, int i) { return ???; }
}
interface B {
int baz(int i);
}
abstract class B$class {
static void $init$(B $this) {}
static int baz(B $this, int i) { return ???; }
}
class C implements A, B {
public C() {
A$class.$init$(this);
B$class.$init$(this);
}
@Override public int baz(int i) { return B$class.baz(this, i); }
@Override public int foo(int i) { return A$class.foo(this, i); }
@Override public int abstractBar(int i) { return ???; }
}
However, Scala 2.12 requires Java 8, and thus is able to use default methods and static methods in interfaces, and the result looks more like this:
interface A {
static void $init$(A $this) {}
static int foo$(A $this, int i) { return ???; }
default int foo(int i) { return A.foo$(this, i); };
int abstractBar(int i);
}
interface B {
static void $init$(B $this) {}
static int baz$(B $this, int i) { return ???; }
default int baz(int i) { return B.baz$(this, i); }
}
class C implements A, B {
public C() {
A.$init$(this);
B.$init$(this);
}
@Override public int abstractBar(int i) { return ???; }
}
As you can see, the old design with the static methods and forwarders has been retained, they are just folded into the interface. The trait's concrete methods have now been moved into the interface itself as static
methods, the forwarder methods aren't synthesized in every class but defined once as default
methods, and the static $init$
method (which represents the code in the trait body) has been moved into the interface as well, making the companion static class unnecessary.
It could probably be simplified like this:
interface A {
static void $init$(A $this) {}
default int foo(int i) { return ???; };
int abstractBar(int i);
}
interface B {
static void $init$(B $this) {}
default int baz(int i) { return ???; }
}
class C implements A, B {
public C() {
A.$init$(this);
B.$init$(this);
}
@Override public int abstractBar(int i) { return ???; }
}
I'm not sure why this wasn't done. At first glance, the current encoding might give us a bit of forwards-compatibility: you can use traits compiled with a new compiler with classes compiled by an old compiler, those old classes will simply override the default
forwarder methods they inherit from the interface with identical ones. Except, the forwarder methods will try to call the static methods on A$class
and B$class
which no longer exist, so that hypothetic forwards-compatibility doesn't actually work.
Source: stackoverflow.com
Related Query
- How are Scala traits compiled into Java bytecode?
- How is Scala faster than Java when both are compiled into `.class` file which is run by JVM only?
- How are scala traits that extend classes compiled for a JVM target?
- What are the differences and similarties between Scala traits vs. Java 8 interfaces?
- How are Java threads heavy compared to Scala / Akka actors?
- How are Scala closures transformed to Java objects?
- Why is a Scala companion object compiled into two classes(both Java and .NET compilers)?
- Why are concrete function implementations in traits compiled to bridge methods in Scala 2.9.x but not in 2.8.x?
- How JVM distinguish between Scala bytecode and Java bytecode?
- How do I convert a Java byte array into a Scala byte array?
- how to decode Java strings with Unicode escapes etc. from Scala JavaTokenParsers into unescaped strings?
- How do I create a Scala Regex that is compiled using Java Pattern.COMMENTS?
- In Scala, how do I mixin java interfaces into Scala code
- In Scala 2.8, how to write (append) a line to a file? Should I use Java clesses or there are native Scala functions for this?
- How to convert a Scala Iterable into Java util.List?
- Experiences with Scala swing, how does it add to what's already available in Java swing? Are there any challenges?
- To integrate scala into java using maven, how do compare available plugins?
- How do I convert Java collections into Scala in Cassandra Datastax driver
- Unable to import compiled java avro files into Scala codebase
- How do I can convert Scala for loop into Java
- How to include hbci4java (a java ANT project hosted on github) into a SBT scala application?
- What's the standard way to work with dates and times in Scala? Should I use Java types or there are native Scala alternatives?
- How to get Scala List from Java List?
- How is pattern matching in Scala implemented at the bytecode level?
- What are the relationships between Any, AnyVal, AnyRef, Object and how do they map when used in Java code?
- Converting a Java collection into a Scala collection
- How to pass Scala array into Scala vararg method?
- How are the multiple Actors implementation in Scala different?
- How to insert double quotes into String with interpolation in scala
- Using Scala traits with implemented methods in Java
More Query from same tag
- Spark scala: How to factorize code with Column?
- cats' NonEmptyList vs scala stdlib ::
- programming scala apps on iphone?
- scala variant type causes error type mismatch error
- How to catch an exception and throw a different exception
- How to implement actor model without Akka?
- Scala usage statistics
- Resolving Akka futures from ask in the event of a failure
- MongoDB Scala driver - problem with Set field
- How to save a JSON file in ElasticSearch using Spark?
- Re-use string that are using on pattern/matched/replace/split Spark function
- Akka-Typed Actor unit tests
- Recursive methods to create Streams in Scala
- QueryStringBinder for List[String]
- Why is my router not receiving check-in events?
- Use of spark to optimize S3 to S3 transfer
- Why do I get a java.nio.BufferUnderflowException in this Scala
- scalatags TypedTag from HTMLElement
- Reading multiple csv files at different folder depths
- Scala - Is there a way to mock trait, extended by companion object?
- Scala equivalent of Python help()
- Update a slice of an array in scala
- Is Atlasian Bamboo 5.x compatible with Play Framework 2.x
- Shapeless HList map after foldRight
- How to write nested loops in idiomatic Scala?
- what `extends (() => Unit)` mean?
- Casbah cas from BasicDBObject to my type
- How to transform a list into a list of pairs like that?
- Any info out there on migrating a Scala 2.9 compiler plugin to 2.10?
- Alternative to load-on-startup Servlets for bootstrapping methods in an webapp