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
orA
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, aList[A]
in Scala may later be aList[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