Back2Basics: The Story of Trait – Part 1


In this blog, we will have a look at traits in Scala.

We have classes in Scala. Suppose we have a class Dog which has a method speak.

class Dog(val name: String){
def speak() = println(s"$name is barking")
}

Now if we want another class Cat which has the same method speak. What we will do?
In Java, generally, we create an interface Animal and classes Dog and Cat will implement the interface. But wait a minute, Scala has no interfaces.

If we try to create an interface Animal which has an abstract method speak, it will throw an error.

scala> interface Animal{
| def speak(): Unit
| }
 error: not found: value interface
interface Animal{
^
error: only classes can have declared but undefined members
def speak(): Unit

Gosh! How we can survive without an interface? Wait a minute, Scala has interfaces but they are not called as an interface, they are called as traits. Let’s try again creating an Animal as a trait.

scala> trait Animal{
| def speak(): Unit
| }
defined trait Animal

Notice this time, there is no error.

So, What is a trait?

At the very basic form, traits are interfaces. Though they are more powerful than interfaces. A trait offers mixins. It is a behaviour that can be mixed into or assimilated into a class hierarchy. So, traits can also have implementations. We will explore them in future blogs.

So, for now, let’s understand how traits are just interfaces.

We have already seen above code worked without an error. Let’s explore this little further. After compiling it, if you see the bytecode generated by Scala Compiler

scala> :javap -c Animal
Compiled from ""
public interface Animal {
public abstract void speak();
}

So, what we found that Animal is actually an interface. Remember, we have written Animal as a trait. But under the hood, it is simply an interface with an abstract method speak because speak is an abstract method and as it returns unit so it became a void method. So, you can see in case of Scala how traits are just interfaces on the JVM.

Now continue with our Dog example,

scala> class Dog(val name: String) extends Animal{
     | def speak() = println(s"$name is barking")
     | }
defined class Dog

scala> class Cat(val name: String) extends Animal{
     | def speak() = println(s"$name is meowing")
     | }
defined class Cat

scala> val jackey = new Dog("Jackey")
jackey: Dog = Dog@2974f221

scala> jackey.speak
Jackey is barking

scala> val kity = new Cat("Kity")
kity: Cat = Cat@42c9b1ee

scala> jackey.speak
Jackey is barking

scala> val kity = new Cat("Kity")
kity: Cat = Cat@edf4973

scala> kity.speak
Kity is meowing

Here, we have Dog and Cat class which extends from Animal and provided the implementation of speak. It is working fine. Let’s see the bytecode generated for classes by Scala Compiler.

javap -c Dog.class
Compiled from "Sample.scala"
public class traitexamples.Dog implements traitexamples.Animal {
public java.lang.String name();
Code:
0: aload_0
1: getfield #15 // Field name:Ljava/lang/String;
4: areturn

public void speak();
Code:
0: getstatic #25 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: new #27 // class java/lang/StringBuilder
6: dup
7: ldc #28 // int 11
9: invokespecial #32 // Method java/lang/StringBuilder."":(I)V
12: aload_0
13: invokevirtual #34 // Method name:()Ljava/lang/String;
16: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #40 // String is barking
21: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #43 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #47 // Method scala/Predef$.println:(Ljava/lang/Object;)V
30: return

public traitexamples.Dog(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #15 // Field name:Ljava/lang/String;
5: aload_0
6: invokespecial #50 // Method java/lang/Object."":()V
9: return
}

Notice even we have used the word extends while extending Animal trait in Dog, implements is not used in class. Despite that, replacement of extend at the bytecode level is still implements.  That’s because Scala compiler knows that Animal is a trait meaning it’s an interface. So, Dog implements the interface named Animal.

Let’s play with it more, Animal could either be Pet or Wild.

scala> trait Pet extends Animal
defined trait Pet

Now let’s see the bytecode of Pet generated by Scala Compiler,

scala> :javap -c Pet
Compiled from ""
public interface Pet extends Animal {
}

Notice this time Pet extends Animal because, in Java, interface extends from other interfaces not implement them. So, we can see it is very consistent with the bytecode when Scala generates the bytecode.

We have seen that at the very minimum, traits are just an interface. But it is a lot more than an interface. In next blog The Story of Trait – Part 2 , we will see traits can have implementation too and how they behave under the hood.

For the complete example, the entire code is available on GitHub.

Please feel free to suggest and comment!

References:

Scala CookBook

Pragmatic Scala


knoldus-advt-sticker


 

About Mahesh Chand

Software Craftsman + Pragmatic Programmer + Explorer + Foodie + Movie Buff
This entry was posted in Functional Programming, Scala and tagged , , , , , . Bookmark the permalink.

6 Responses to Back2Basics: The Story of Trait – Part 1

  1. Pingback: Back2Basics: The Story of Trait – Part 2 | Knoldus

  2. Pingback: ScalaFP: Understanding Semigroups In Scala Pragmatically | Knoldus

  3. Pingback: Scala FP: Let’s Start with Type Class | Knoldus

  4. Pingback: Limit which classes a Trait can be mixed into! – Divya's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s