OOP Basics

The purpose of this workshop is to understand scala OOP part from Java Developer Perspective. We will see many examples on how easy it is to call java code from scala thus knowledge about java libraries is still valuable and you don't need to re-learn everything from scratch.

Declaring Classes

In this part we will see :

  • How to declare a class in scala
  • How to use java classes in scala
  • Java/Scala Byte Code Comparison
  • Constructors in scala
  • Final values and variables
  • Private state in classes

FILE : StarterOOP1ClassesInScalaDemo

Java classes can be used directly in scala code. In this first example we will Explain each part of scala declaration syntax.

val userJava:UserJava=new UserJava("Zdzislawa", 25)

Main differences from java

  • type is on the right side of a variable name
  • type can be interferred by compiler
  • no semicolons
  • val is like final in java

Compilation - Simple Classes Comparison

Because in scala you can put many public classes into an one file so our single demo file generate multiple classes. Let's compare interface generated from java code with the scala counterpart.

ls -l target/scala-2.12/classes/jug/lodz/workshops/starter/oop1/

(...)
UserJava.class
UserScala.class

Compare amount of code between Java and Scala.

Scala Code :

class UserScala(val name: String, val age: Int) {
  override def toString = s"ScalaUser($name, $age)" // String interpolation!
}

Java Code:

public class UserJava {
    private String name;
    private int age;

    public UserJava(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "UserJava{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Both generate similar bytecode

javap  target/scala-2.12/classes/jug/lodz/workshops/starter/oop1/UserScala.class 
Compiled from "StarterOOP1ClassesInScalaDemo.scala"

public class jug.lodz.workshops.starter.oop.UserScala {
  public java.lang.String name();
  public int age();
  public java.lang.String toString();
  public jug.lodz.workshops.starter.oop.UserScala(java.lang.String, int);
}

Constructors

Whole body of a class is a constructor so in the example below println will be executed when class is being created.

class Example(_variable1:String){  //no val
      println("\n *** this is constructor in Example class")

      def variable=_variable1  //getter
      //TOME - explain method basics
      def getVariable={
        println("log")
        _variable1 //last statement is return , don't use return build in scala!!!
      }
    }

Setters

change val to var

class UserScala(var name: String, val age: Int) {
  override def toString = s"ScalaUser($name, $age)" // String interpolation!
}
public class jug.lodz.workshops.starter.oop.UserScala {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public java.lang.String toString();
  public jug.lodz.workshops.starter.oop.UserScala(java.lang.String, int);
}

State encapsulation

Just put private in front of val/var/def.

//More similar to classical java
    class ExamplePrivateVariables{
      private var variable:String = ""
      def setVariable(v:String):Unit= variable = v
    }

If you add val before parameters in class signature, then they will be visible outside the class

class UserScala(val name: String, val age: Int) {
  override def toString = s"ScalaUser($name, $age)" // String interpolation!
}

 val scalaUser: UserScala =new UserScala("Bogumila",18)
scalaUser.name //Bogumila

Interfaces, Traits & Objects

FILE : StarterOOP1ObjectsDemo

In this part we will see:

  • Scala classes implementing Java interfaces
  • Calling java static methods from scala
  • Scala Object concept

Traits with abstract methods are compiled to simple java interfaces

trait ScalaInterface{
  def toJson():String
}
javap  target/scala-2.12/classes/jug/lodz/workshops/starter/oop1/ScalaInterface.class 
Compiled from "StarterOOP1ObjectsDemo.scala"

public interface jug.lodz.workshops.starter.oop1.ScalaInterface {
  public abstract java.lang.String toJson();
}

Then a class extending this interface (there is no implements keyword in scala) is again compiled to simple java class with interface implementation

class SecondImplementation(value:String) extends ScalaInterface{
  override def toJson(): String = s"""{"value" : '$value'}"""
}
javap  target/scala-2.12/classes/jug/lodz/workshops/starter/oop1/SecondImplementation.class 
Compiled from "StarterOOP1ObjectsDemo.scala"

public class jug.lodz.workshops.starter.oop1.SecondImplementation implements jug.lodz.workshops.starter.oop1.ScalaInterface {
  public java.lang.String toJson();
  public jug.lodz.workshops.starter.oop1.SecondImplementation(java.lang.String);
}

IMPORTANT

If we want to set instance type as an interface type then we must declare type explicitly. Without explicit declaration scala compiler will infer concrete class type.

val scalaImplementation:JavaInterface=new ScalaImplementation("scalaValue")

Objects

You can easily call static java methods from scala code yet scala doesn't have static keyword itself. To achieve similar behaviour we can create singleton object

object FactoryExample{
  def factoryMethod(v:String):ScalaInterface = new SecondImplementation(v) //single letter variables and "clean code"
  def apply(v:String):ScalaInterface = new SecondImplementation(v)
}

There is always only one instance of this object and it is a very good place to define factory methods for general class instances.

public final class jug.lodz.workshops.starter.oop1.FactoryExample$ {
  public static jug.lodz.workshops.starter.oop1.FactoryExample$ MODULE$;
  public static {};
  public jug.lodz.workshops.starter.oop1.ScalaInterface factoryMethod(java.lang.String);
  public jug.lodz.workshops.starter.oop1.ScalaInterface apply(java.lang.String);
}


public final class jug.lodz.workshops.starter.oop1.FactoryExample {
  public static jug.lodz.workshops.starter.oop1.ScalaInterface apply(java.lang.String);
  public static jug.lodz.workshops.starter.oop1.ScalaInterface factoryMethod(java.lang.String);
}

Method apply is very special in scala because we can actually omit its name achieving simpler code (this is particularly useful whit functions) :

val fromApply: ScalaInterface =FactoryExample.apply("applyValue")
val fromApplySugar=FactoryExample("applySynthaticSugar")

Example - ScalaTest

FILE : StaticMethodsTest

ScalaTest is one of the most popular testing frameworks in scala. It is a great ilustration of many concepts trained during this workshop.

You declare a test by extending Spec class and by mixing necessary traits.

class StaticMethodsTest extends WordSpec with MustMatchers

In this example we can see that it is actually very easy to test java classes with scala framework. So it may be a way to start with scala as a testing framework for java classes.

Also we will see an instance of so called inflix operator (method without dot and parenthesis looks like an operator)

result.mustBe("prefix_test")
result mustBe "prefix_test"

run tests in IDEA - ctrl+shift+f10

Exercise

1) Test Java class

  • replace ??? with valid scala instructions to make code compile and test passing
  • when first test is ready uncomment second one

FILE : exercises.TestingJavaClassExercise

2) Statefull Instance in Scala

  • Implement similar logic in scala as it was in previous exercise in Java - class with general state. Implement trait HasState which works like a standard interface.
  • In the second part try to save changes history - this one is HARD when you are beginner but still worth to try. History will be kept in standard java.util.Collection

FILE : exercises.StatefulInstanceInScalaExercise

3) FACTORY METHOD

You have provided an interface for processing strings:

trait Processor{
  def process(input:String):String
}
  • implement two processors : one changes string to upper and seconds trim whitespaces

  • implement object factory which returns processor according to string id -declare id's according to tests

FILE : exercises.FactoryMethodExercise

results matching ""

    No results matching ""