MyWorldGo Master Scala Now: 5 Crucial Differences Between Abstract Classes and Traits You Can't Afford to Miss!

Blog Information

  • Posted By : Ava Parker
  • Posted On : Oct 22, 2024
  • Views : 28
  • Category : Technology
  • Description : To grasp the distinction between an abstract class and a trait, we first need to understand each concept.

Overview

  • 1. Introduction  
    To understand the difference between an abstract class and a trait, we must first define each term. Traits are similar to Java interfaces, but they allow for the implementation of their members. Abstract classes, on the other hand, are created using the `abstract` keyword and can include both abstract and concrete methods.

    2. Commonalities  
    Identifying the similarities helps clarify the differences. Let’s examine these features.  
    A trait that has a declared and implemented method along with a variable:  
    scala
    trait mytrait { 
      def myMethod(): Unit
      val aVal: Int 
      val myVal = 10 
      def myImpMethod = { println("I am from trait") } 
    }

    An abstract class that contains both an abstract and a concrete method along with a variable:  
    scala
    abstract class abstClass { 
      def myMethod(): Unit 
      val aVal: Int 
      val myVal = 10 
      def myImpMethod: Unit = { println("I am from abstClass") } 
    }

    In the main method, neither a trait nor an abstract class can be instantiated:  
    scala
    val myTObj = new mytrait // compilation error
    val myAObj = new abstClass() // compilation error

    Classes that extend a trait or an abstract class must implement the declared methods:  
    scala
    class myTClass extends mytrait {
      val aVal = 10 
      def myMethod = { println("I am from myTclass") } 

    class myAClass extends abstClass {
      val aVal = 10 
      def myMethod = { println("I am from myAclass") } 
    }


    3. Distinctions  
    3.1 Constructor Parameters  
    scala
    trait myTraitWithParam(name: String){} 
    abstract class abstClassWithParam(name: String){}

    The `name` serves as a constructor parameter; using a constructor parameter in a trait leads to a compilation error, while it works correctly in an abstract class.

    3.2 Modifying Object Instances  
    scala
    trait objectInstanceTrait { 
      def myObjectMethod = { println("I originate from the object instance trait") } 

    class myTClass {} 
    def main(args: Array[String]): Unit = { 
      val classObjWithTrait = new myTClass with objectInstanceTrait
      classObjWithTrait.myObjectMethod 


    Output: "I originate from the object instance trait"  
    The method `myObjectMethod` from the trait can be accessed through a class instance. An abstract class cannot be mixed into an object instance, resulting in a compilation error.

    3.3 Multiple Inheritances  
    In multiple inheritance, a single class can inherit from more than one superclass, gaining features from all parent classes. While Scala does not allow multiple inheritance with classes, it can be achieved using traits:  
    scala
    class myClass extends trait1 with trait2{} 
    class myNewClass extends abstClass1 with abstClass2{}

    Using an abstract class for multiple inheritances results in a compilation error. Scala resolves the diamond problem in multiple inheritance through trait linearization, where the rightmost trait takes precedence:  
    scala
    trait Printer { 
      def print(msg: String) = println(msg) 

    trait DelimitWithHyphen extends Printer { 
      override def print(msg: String) = { println("-------------") } 

    trait DelimitWithStar extends Printer { 
      override def print(msg: String) = { println("*") } 


    class CustomPrinter extends Printer with DelimitWithHyphen with DelimitWithStar 

    new CustomPrinter().print("Hello World!") 

    Output: "*"  

    3.4 Stackability  
    Stackable traits in Scala allow for the combination of multiple traits that collaboratively modify a method. This involves calling `super.theMethod`:  
    scala
    trait base { 
      def baseMethod(s: String): Unit = println(s) 
    }

    trait stack1 extends base {
      override def baseMethod(s: String): Unit = println("from stack1") 


    trait replace1 extends base { 
      abstract override def baseMethod(s: String): Unit = {
        super.baseMethod(s); 
        println("from replace1")
      } 
    }

    class Stackable { 
      this: base => 
      def apply() { 
        println(baseMethod("bottom")) 
      } 


    (new Stackable with stack1 with replace1)() 

    Output:  
    from stack1  
    from replace1  
    The invocation starts with `replace1`, which then calls `stack1` via `super.baseMethod(s)`.

    3.5 Interoperability with Java  
    Traits can seamlessly integrate with Java as long as they lack any implementation; abstract classes can be used directly.