home search about rss feed twitter ko-fi

A Look at Java/OOP Code and Data Types (Scala 3 Video)

Part 1: A Look at Java/OOP Code and Data Types

(This part corresponds to Chapter 3 in my book, Learn Functional Programming The Fast Way.)

In my Java programming days I rarely used generic data types, and in fact, I rarely thought about data types much at all. To demonstrate why, I’ll show one of my common examples: writing a point-of-sales application for a pizza store.

Two points before we move on:

  • A generic type in Java and Scala is typically a character like T or A that’s used in a method declaration. You use a generic type to say, “The exact type used here will be filled in later.” For instance, a List[A] in Scala may later be a List[Int], List[String], etc. I write more about generic types as this book goes on.
  • In these examples, please note that my Java code may not be 100% correct. I haven’t put this code in an IDE or tried to compile it. Because I assume that you know Java or other OOP languages, my intent is to just quickly demonstrate the Java/OOP approach I used for many years.

Modeling a pizza store application

Some enums

To start modeling a point-of-sales application for a pizza store, I’ll first need my usual enumerations:

enum Topping {
    Cheese,
    Pepperoni,
    BlackOlives
}

enum CrustSize {
    Small,
    Medium,
    Large
}

enum CrustType {
    Regular,
    Thin,
    Thick
}

A pizza class

Given those, let’s create a Pizza class. But before doing that, let’s list the attributes and behaviors of an OOP pizza. The attributes are basically what I listed with those enums:

  • Crust size
  • Crust type
  • Types of toppings

The behaviors correspond to those attributes:

  • Add topping
  • Remove topping
  • Set crust size
  • Set crust type

If I also kept price-related information, a pizza could also calculate its own price, but since the toppings and crust attributes don’t carry price information with them, that’s pretty much all of the behaviors of this Pizza.

Having gone through this exercise several times before, I know that I want all of the food-related items in a pizza store — pizza, breadsticks, soda, etc. — to extend a base Product data type:

public interface Product {}

In the real world, a Product will have attributes like cost, and potentially a sales price, but for the purposes of this exercise I’ll just leave it as a marker interface like that.

Given this Product — and the attributes and behaviors of my Pizza — the initial code for a Java/OOP Pizza class looks like this:

public class Pizza implements Product {

    private List<Topping> toppings = new ArrayList<>();
    private CrustSize crustSize;
    private CrustType crustType;

    public Pizza(CrustSize crustSize, CrustType crustType) {
        this.crustSize = crustSize;
        this.crustType = crustType;
    }

}

That Pizza class will then include the following series of getter and setter methods, with these type signatures:

public CrustSize getCrustSize()
public CrustType getCrustType()
public List<Topping> getToppings()

public void setCrustSize(CrustSize crustSize)
public void setCrustType(CrustType crustType)
public void setToppings(List<Topping> toppings)

The term type signature refers to a function’s (a) input parameters and (b) return type.

Discussion

Notice that this is all simple code. The getters don’t take any input parameters and they return basic data types, and the setters all return void. I wrote Java code like this for more than 12 years.

In fact, if I wrote more code, such as for an Order class, you’d see that the pattern is the same, all very simple code:

public class Order {

    private List<Product> lineItems = new ArrayList<>();
    public Order() {}

    public void addItem(Product p)
    public void removeItem(Product p)
    public List<Product> getItems()

    public String getPrintableReceipt()
    public BigDecimal getTotalPrice()

}

The closest I get to using generic types in that code are these types:

List<Topping>
List<Product>

Part 2: Java/OOP Summary

(This part corresponds to Chapter 4 in my book, Learn Functional Programming The Fast Way.)

That was a quick look at a little Java/OOP code that I used to use when teaching Java courses. Of course I didn’t show much; just enough to get the basic idea across.

A First Summary

To summarize what you saw in that code:

  • My Java/OOP code was simple, with no generic types
  • A lot of methods return void
  • A lot of methods have no input parameters
  • I always use mutable data structures

Mutable data structures are structures like the Java ArrayList, which can be resized, and whose elements can be updated. We say that a structure like this can be mutated, which just means “changed.”

A Second Summary

There are a few other things to say about that code, and bear in mind, I’m only talking about my own code.

POINT 1: I didn’t realize it when I was writing Java/OOP code, but all of those methods that return void and take no input parameters tend to warp your brain. You can’t tell what the methods do by looking at their type signatures, so you either (a) trust that the methods are well-named, or (b) you have to look at their source code to see what they do.

As an example of what I mean, I trusted my own code because I knew that all of my getters and setters were just boilerplate code. But one time when I was debugging a problem with a junior developer, I found that he wrote a “simple” setter method like this:

public void setFoo(Foo newFoo) {
    openTheGarageDoor();
    walkTheDog();
    buyGroceries();
    solveWorldPeace();
    this.foo = newFoo + 1;
}

It wasn’t quite that exhaustive, but there was a lot going on in there. And bear in mind, all of that happened inside a “simple setter method” with this type signature:

public void setFoo(Foo newFoo)

The lessons I took away from that were:

  • This developer needed some more training
  • I couldn’t trust method type signatures, I had to look at their code

POINT 2: A second point that I don’t show in my pizza example is that many Java methods can throw exceptions, and when they can, you have to think about (a) handling the “success” case, and (b) handling the “failure” case, which for me meant wrapping the method call with try/catch.

This gets to be ridiculously hard on the brain, so I got to the point that — using the Model/View/Controller (MVC) paradigm — I only handled exceptions with my top-level controllers. I logged exceptions at the point they occurred, but then just let them bubble up to my controllers, and handled them there.

At that time I didn’t know anything about functional programming, and I couldn’t think of a better way to deal with potential errors — invalid data, servers being down, etc. — so that’s how I dealt with exceptions.

Related

For more details on my MVC approach, see my blog posts:

Update: All of my new videos are now on
LearnScala.dev