DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • What To Know Before Implementing IIoT
  • The Practical Difference Between Abstract Class and Trait in Scala
  • The Mystery of Traits in Scala
  • SaaS in an Enterprise - An Implementation Roadmap

Trending

  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Build Your First AI Model in Python: A Beginner's Guide (1 of 3)
  • Analyzing Techniques to Provision Access via IDAM Models During Emergency and Disaster Response
  • Non-Project Backlog Management for Software Engineering Teams
  1. DZone
  2. Coding
  3. Languages
  4. Scala Traits Implementation and Interoperability. Part I: Basics

Scala Traits Implementation and Interoperability. Part I: Basics

By 
Tomasz Nurkiewicz user avatar
Tomasz Nurkiewicz
DZone Core CORE ·
Apr. 11, 13 · Interview
Likes (0)
Comment
Save
Tweet
Share
8.1K Views

Join the DZone community and get the full member experience.

Join For Free

 Traits in Scala are similar to interfaces, but much more powerful. They allow implementations of some of the methods, fields, stacking, etc. But have you ever wondered how are they implemented on top of JVM? How is it possible to extend multiple traits and where the implementations go to? In this article, based on my StackOverflow answer, I will give several trait examples and explain how scalac implements them, what are the drawbacks and what you get for free. Often we will look at compiled classes and decompile them to Java. It's not essential knowledge, but I hope you'll enjoy it. All examples are compiled against Scala 2.10.1.

Simple trait with no method implementations

The following trait:
trait Solver {
    def answer(question: String): Int
}
compiles down to the most boring Java interface:
public interface Solver {
    int answer(String);
}
There is really nothing special in Scala traits. That also means that if you ship Solver.class as part of your library, users can safely implement such interface from Java code. As far as java/javac is concerned, this is an ordinary Java compiled interface.

Traits having some methods implemented

OK, but what if trait actually has some method bodies?
trait Solver {
 
    def answer(s: String): Int
 
    def ultimateAnswer = answer("Answer to the Ultimate Question of Life, the Universe, and Everything")
 
}
Here, ultimateAnswer method actually has some implementation (the fact that it calls abstract answer() method is irrelevant) while answer() remains unimplemented. What will scalac produce?
public interface Solver {
    int answer(java.lang.String);
    int ultimateAnswer();
}
Well, it's still an interface and the implementation is gone. If the implementation is gone, what happens when we extend such a trait?
class DummySolver extends Solver {
 
    override def answer(s: String) = 42
 
}
We need to implement answer() but ultimateAnswer is already available via Solver. Anxious to see how it looks under the hood? DummySolver.class:
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;
    }
 
}
That... is... weird... Apparently we missed one new class file, Solver$class.class. Yes, the class is named Solver$class, this is valid even in Java. This is not Solver.class expression which returns Class[Solver], it's apparently an ordinary Java class named Solver$class with a bunch of static methods. Here is how it looks like:
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) {}
}
Do you see the trick? Scala created a helper Solver$class and methods that are implemented inside trait are actually placed in that hidden class. BTW this is not a companion object, it's just a helper class invisible to you. But the real trick is $this parameter, invoked as Solver$class.ultimateAnswer(this). Since we are technically in another object, we must somehow get a handle of a real Solver instance. It is like OOP but done manually.

So we learned that method implementations from traits are extracted to a special helper class. This class is referenced every time we call such a method. This way we don't copy method body over and over into every single class extending given trait.

Extending multiple traits with implementations

Imagine extending multiple traits, each implementing distinct set of methods:
trait Foo {
    def foo = "Foo"
}
 
trait Bar {
    def bar = "Bar"
}
 
class Buzz extends Foo with Bar
By analogy you probably know already how Foo.class and Bar.class look like:
public interface Foo {
    String foo();
}
 
public interface Bar {
    String bar();
}

Implementations are hidden in mysterious ...$class.class files just as before:
public abstract class Foo$class {
    public static String foo(Foo) {
        return "Foo";
    }
 
    public static void $init$(Foo) {}
}
 
public abstract class Bar$class {
    public static String bar(Bar) {
        return "Bar";
    }
 
    public static void $init$(Bar) {}
}

Notice that static methods in Foo$class accept instances of Foo while Bar$class require reference to Bar. This works because Buzz implements both Foo and Bar:
public class Buzz implements Foo, Bar {
 
    public Buzz() {
        Foo.class.$init$(this);
        Bar.class.$init$(this);
    }
 
    public String bar() {
        return Bar$class.bar(this);
    }
 
    public String foo() {
        return Foo$class.foo(this);
    }
 
}

Both foo() and bar() pass this reference, but this is fine.

Traits with fields

Fields are another interesting feature of traits. This time let's use a real-world example from Spring Data project. As you know interfaces can require certain methods to be implemented. However they can't force you to provide certain fields (or provide fields on their own). This limitation becomes painful when working with Auditable<U, ID> interface extending Persistable<ID>. While these interfaces merely exist to make sure certain fields are present on your @Entity class, namely createdBy, createdDate, lastModifiedBy, lastModifiedDate and id, this cannot be expressed cleanly. Instead you have to implement the following methods in every single entity extending Auditable:
       U getCreatedBy();
    void setCreatedBy(final U createdBy);
DateTime getCreatedDate();
    void setCreatedDate(final DateTime creationDate);
       U getLastModifiedBy();
    void setLastModifiedBy(final U lastModifiedBy);
DateTime getLastModifiedDate();
    void setLastModifiedDate(final DateTime lastModifiedDate);
 
      ID getId();
 boolean isNew();
Moreover, every single class must of course define fields highlighted above. Doesn't feel DRY at all. Luckily traits can help us a lot. In trait we can define what fields should be created in every class extending this trait:
trait IAmAuditable[ID <: java.io.Serializable] extends Auditable[User, ID] {
 
    var createdBy: User = _
 
    def getCreatedBy = createdBy
 
    def setCreatedBy(createdBy: User) {
        this.createdBy = createdBy
    }
 
    var createdDate: DateTime = _
 
    def getCreatedDate = createdDate
 
    def setCreatedDate(creationDate: DateTime) {
        this.createdDate = creationDate
    }
 
    var lastModifiedBy: User = _
 
    def getLastModifiedBy = lastModifiedBy
 
    def setLastModifiedBy(lastModifiedBy: User) {
        this.lastModifiedBy = lastModifiedBy
    }
 
    var lastModifiedDate: DateTime = _
 
    def getLastModifiedDate = lastModifiedDate
 
    def setLastModifiedDate(lastModifiedDate: DateTime) {
        this.lastModifiedDate = lastModifiedDate
    }
 
    var id: ID = _
 
    def getId = id
 
    def isNew = id == null
}

But wait, Scala has built-in support for POJO-style getters/setters! So we can shorten this to:
class IAmAuditable[ID <: java.io.Serializable] extends Auditable[User, ID] {
 
    @BeanProperty var createdBy: User = _
 
    @BeanProperty var createdDate: DateTime = _
 
    @BeanProperty var lastModifiedBy: User = _
 
    @BeanProperty var lastModifiedDate: DateTime = _
 
    @BeanProperty var id: ID = _
 
    def isNew = id == null
}
Compiler-generated getters and setters implement interface automatically. From now on, every class willing to provide auditing capabilities can extend this trait:
@Entity
class Person extends IAmAuditable[String] {
 
    //...
 
}
All the fields, getters and setters are there. But how is it implemented? Let's look at the generated Person.class:
public class Person implements IAmAuditable<java.lang.String> {
    private User createdBy;
    private DateTime createdDate;
    private User lastModifiedBy;
    private DateTime lastModifiedDate;
    private java.io.Serializable id;
 
    public User createdBy() //...
    public void createdBy_$eq(User) //...
    public User getCreatedBy() //...
    public void setCreatedBy(User) //...
 
    public DateTime createdDate() //...
    public void createdDate_$eq(DateTime) //...
    public DateTime getCreatedDate() //...
    public void setCreatedDate(DateTime) //...
 
    public boolean isNew();
 
    //...
}
There is actually much more, but you get the idea. So fields aren't refactored into a separate class, but that was to be expected. Instead fields are copied into every single class extending this particular trait. In Java we could have used abstract base class for that, but it's nice to reserve inheritance for real is-a relationships and do not use it for dummy field holders. In Scala trait is such a holder, grouping common fields that we can reuse across several classes.

What about Java interoperability? Well, IAmAuditable is compiled down to Java interface, thus it doesn't have fields at all. If you implement it from Java, you gain nothing special.

We covered basic use cases for traits in Scala and their implementation. In the next article we will explore how mixins and stackable modifications work.


Trait (computer programming) Implementation Scala (programming language) Interoperability

Published at DZone with permission of Tomasz Nurkiewicz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • What To Know Before Implementing IIoT
  • The Practical Difference Between Abstract Class and Trait in Scala
  • The Mystery of Traits in Scala
  • SaaS in an Enterprise - An Implementation Roadmap

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!