Eli-Golin
3/7/2016 - 9:28 AM

Traits

Traits

//The simplest trait
trait Solver {
    def answer(question: String): Int
}

//compiles down to the most borring java interface
public interface Solver {
    int answer(String);
}

//traits having some method implementation
trait Solver {

    def answer(s: String): Int

    def ultimateAnswer = 
      answer("Answer to the Ultimate Question of Life, the Universe, and Everything")

}

//Still just regular interfacer is generated
public interface Solver {
    int answer(java.lang.String);
    int ultimateAnswer();
}

//The magic occurs when we extend such trait
class DummySolver extends Solver {

    override def answer(s: String) = 42

}

//This is how that class actually looks like
public class DummySolver implements Solver {

    public DummySolver() {
        Solver$class.$init$(this);
    }

    public int ultimateAnswer() {
        return Solver$class.ultimateAnswer(this);
    }

    public int answer(String s) {
        return 42;
    }

}

// A new Solver$class is generated behind the scenes with a bunch of 
//static methods.
//This static class receives an instance of the Solver.
//The general scala's patern to implement methods in traits, is to create a helper
//class with static methods and pass in the instance (this) of the class implementing
//the trait -- (kind like implementing OOP manually)
public abstract class Solver$class {

    public static int ultimateAnswer(Solver $this) {
        return 
          $this.answer("Answer to the Ultimate Question of Life, the Universe, and Everything");
    }

    public static void $init$(Solver solver) {}
}

// How fields are implemented?
trait A {
  val f1 = 1
}

class B extends TraitWithField

//Is compiled into 
public interface A {

    public abstract void A$_setter_$f1_$eq(int i);

    public abstract int f1();
}

public abstract class A$class {

    public static void $init$(A $this){
        $this.A$_setter_$f1_$eq(1);
    }
}

public class B implements A {

    public int f1() {
        return f1;
    }

    public void A$_setter_$f1_$eq(int x$1) {
        f1 = x$1;
    }

    public B() {
        A.class.$init$(this);
    }

    private final int f1;
}