Why Java?

This course will be primarily taught in the Java Programming language. Many students are often under the impression that Java is a “dead programming language”, or that we should use something like Javascript/Typescript. This article lays out my reasons for using Java, as opposed to those languages, in this course.

Note that in future semesters, I may switch programming languages in CS 3140. I’m not saying I will, but at least consider the idea each summer. But this a justification for why I’m using Java in the current semester (whatever semester you are reading this).


Contents


Reasons for Java

The following is a brief list of the reasons Java was selected for CS 3140. These reasons are in order how much they affected the decision to stick with Java.


Reason 1. Experience

This course follows CS 2100: Data Structures and Algorithms 1 at the University of Virginia, which teaches students Java as well as several foundational object-oriented programming concepts. As such, students will have worked with classes and common data structures in the Java programming language. Many students, but not all students, will have also learned Python while taking CS 111x. However, students did not dive into object-orientation in Python in this class. Because I can assume most students taking this course know Java, it makes it the safest language to utilize.


Reason 2: Object Orientation

Object-oriented design is the prevailing design paradigm for large, complex software systems. In this class, we will discuss software design primarily from an object-oriented perspective. This includes concepts like inheritance structures, interfaces, and polymorphism. Software Design patterns are largely object-oriented, and many refactoring techniques are designed for object-oriented programming.

A note that Javascript, which is a popular student requested language to teach, has significant limitations with respect to object-oriented programming. Specifically, when we wish to use inheritance in Javascript, we can only do so in ways that violate encapsulation. Typescript, which builds on top of JavaScript, does have support for inheritance (though I’m not personally a fan of the way interfaces work in Typescript)

Another popular request is Python. However, while Python does support classes and polymorphism, Python is primarily built around flexibility rather than maintainability. For example, in Python, you can dynamically add new fields to instances of a class without modifying the class itself. While this sounds useful, usage of this feature will result in less maintainable and more difficult to understand code. Python also doesn’t support strict encapsulation. For instance, there are no such things as private fields or methods in Python. A widely understood convention is to use underscores to indicate methods and variables intended to be private. However, these amount to a “polite request”, as there is no syntactic enforcement of privacy, which violates the idea of encapsulation. Also, while petty, I personally cannot stand the way self gets used in Python.


Reason 3: Code Readability

Java can be a very verbose language. There is a downside this, in that it typically takes more code to write something in Java than it does to write functionally identical in something like Python. However, this can also result in the code being more readable and understandable, as this encourages good variable names, clear communication of data types, and clearer traceability (the ability to find which classes/functions relate to a specific concept).


Reason 4: Support

Java was publicly released over two decades ago, and has been a widely adopted and taught language. Newer programming languages typically have less support. Note that when I say “support”, I do not just mean support from the maintainer of the language, official documentation, etc. I mean support through third party websites, whether they are blog style like w3schools, or question/answer style like StackOverflow. Additionally, Java has a very large set of third party libraries, and you can find libraries for almost anything in Java on the Maven Central Repository.


Reason 5: Popularity

Java is still one of the most in-demand languages for employment. When I have looked at different studies of multiple job posting boards, Java is typically 3rd or 4th – it’s also worth noting that the number 1 language is SQL, which we will learn the basics of in this class. Java tends to be among the three most popular programming language for application development, alongside Python and Javascript. Java is the third highest tagged language on Stack Overflow, meaning there is a very large community of developers interacting with and helping each other. Additionally, Java is still heavily used in Android application development (though for more on that, see Kotlin below).


Reason 6: Portability

As we will discuss later in this unit, Java programs run on the JVM (Java Virtual Machine), which helps ensure a high level of portability. That is, I can be very confident that any code I write when teaching the class can be easily redistributed and will run the same on your machine, whether you are run Mac, PC, or Linux as your primary operating system. This also ensures that teams can have Mac and PC users developing on their preferred platform seamlessly. We will discuss how Java uniquely allows distributable files (like .jar files) to work on any operating system without need for recompilation during this course.


What about Kotlin?

For a while, I considered using Kotlin in this class, as it has many of the same advantages of Java I list below, is interoperable with Java, and has a much more concise syntax. Unfortunately, it would also mean spending class time re-teaching prerequisite knowledge like classes, conditional, functions, loops, etc. rather than learning new material. Ultimately, I found it would simply add too much accidental complexity to the course. Also, because Kotlin is rather new, releasing version 1.0 in 2016, it doesn’t have nearly the established community that Java does. For example, at the time of this writing (Fall 2022), Java has over 1.8 million tagged questions on Stack Overflow, while Kotlin is just over 75,000 (~4%).

I like Kotlin quite a bit, and it is gaining popularity in the Android developer community. Our IDE of choice in this course, IntelliJ, is also natively setup to work with Kotlin just as well as Java. However, these benefits didn’t outweigh the cost of having to learn a different syntax in a class where the primary goal is teaching design concepts. In short, the overhead cost of introducing a new programming language isn’t worth the time cost when I only get ~35 hours of lecture time each semester.

I may reconsider this decision in the future, and would eventually like to see a larger move towards Kotlin, as it has many of the advantages of Java with regards to stability and clarity, but without some of the drawbacks as stated below. One key advantage of Kotlin as a language is that it is complete interoperable with Java, as both use the Java JVM. For instance, a Kotlin program can utilize Java libraries (although it sometimes can result in some awkward syntax). Several major companies, including Google, have moved new Android development to Kotlin in lieu of Java. So it’s a language worth keeping an eye on, if not learning yourself!

Other JVM Languages

There are a large number of languages that use Java’s JVM. In addition to Kotlin, there are:

  • Groovy - A scripting language designed to be similar to languages like Python and Ruby
  • Scala - Similar to Kotlin, Scala is meant to address several of the downsides of Java, especially its lack of conciseness. Scala also more naturally supports functional programming, however Java has added better support for functional programming since Scala released.
  • Clojure - Clojure is a dialect of Lisp designed to work on the JVM. Lisp is a functional language model that has a substantially different syntax than imperative languages like Java, C, C++, Python, etc. I’m a big proponent of learning at least one Lisp-like languages (Common Lisp, Clojure, Scheme, etc.) as I find it helps you more deeply understand functional programming. However, it also has a steeper learning curve than many other languages.

Downsides of Java

The above reasons for Java is not to say Java is perfect; it has flaws like every programming language. In the interest of fairness, I list some of them here:

Downside 1: Verbose

In Reason 3 in favor of Java I note there are advantages to writing in a programming language that forces more verbosity. This is especially true in large programs that must evolve overtime. Java’s rigorous attachment to object-oriented programming can be useful. However, it can also make doing simple things overly complicated or wordy. For example, consider the traditional “Hello World” program below:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Compare this to Python:

print("Hello, World!")

A big part of this cost is that Java forces everything to be in a class. In the Java HelloWorld program, the first two lines don’t do anything, they just establish the context of “when you run this class, run this function.”

The second part of this cost is that Java is statically typed, and enforces that all variables be declared with an explicit data type that cannot be changed. While this may seem like a hurdle compared to the more dynamic typing of a language like Python, there are significant design and readability advantages to this. Additionally, dynamic typing actually comes with a substantial computational cost, which helps explain why Python is notoriously slow.

Ultimately, for an introductory design course, I believe the advantages that Java gives with readability outweigh this cost.

Update: A quick note that Java 21 has introduced a preview of unnamed classes. Meaning if enabled (and by default in the future if the feature is accepted), HelloWorld.java may look like:

void main() {
    System.out.println("Hello, World!");
}

I’m actually excited for a procedural code leaning Java feature. Does this solve all of Java’s verbosity problems? God, no. Opening a file is still going to require a giant BufferedReader initialization somewhere. But this helps.

Downside 2: It’s not “The New Thing”

Java peaked in market share probably around 2005-ish, when Javascript and Python began their ascent. However, as mentioned before, this doesn’t mean Java is going the way of Fortran and Pascal just yet: Java is still very close to Javascript and Python in popularity for application development. Java (or JRE based languages like Kotlin) are not going anywhere, thanks partially due to Android, but largely due to Java’s scalability.

The truth, however, is that “the next language” is always going to come eventually. In the 60s and 70s, everyone was learning Fortran and COBOL. By the 1980, everyone was using Pascal and BASIC. By the mid 80s, the most popular languages were C and Ada. By the mid 90s, Ada was even less popular than Fortran. I wouldn’t be surprised if C was the only language in this paragraph you’ve learned or ever will learn (though COBOL is still fairly widely used in business and finance).

The one constant is change. And one day, they’ll be the new hot language that everyone is using, and Python and Javascript will the languages dinosaurs and old people like you use. So yes, everyone wants to learn javascript because of React and Node.js. And you should learn it. However, with practice, you’ll find that each new language is generally easier to learn than the last.

Downside 3: Checked Exceptions

One common complaint about Java is how much the languages forces error checking. Bob Martin, the author of the book Clean Code which we will reference later this term, wrote nearly all the code in the book in Java. Yet, in regard to Checked Exceptions, he said the following:

When checked exceptions were introduced in the first version of Java, they seemed like a great idea…and yes, they can yield some benefit.

…in general application development, the dependency costs [of checked exceptions] outweigh the benefits.

The purpose of exceptions are for a programmer to communicate an error state which can be handled “at a distance”. For example, a function may throw an exception that is handled several functions higher in the call stack, without having to return a custom selected “error value” from one function to another. Checked exceptions force you to either handle the exception locally, which often isn’t possible, or change a bunch of existing method signatures to add throws FileNotFoundException, IOException. Neither is particularly great for design.

It also leads to silly try-catch blocks

    public List<Data> getDataFromFile(String filename) {
        try {
            //the actual code I care about
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

All this code does is turn a checked exception (IOException, which includes FileNotFoundException) into a RuntimeException. Now, if your program can handle, say, a FileNotFoundException in some meaningful way (such as telling a user a filename they entered doesn’t exist and letting them try again), obviously you’ll have a meaningful catch block that is worth writing. But the fact that Java forces you to either handle an exception or add a throws declaration (thus forcing someone else to handle it or declare it, and so on and so on) is probably my least favorite thing about the entire language.

You know how most languages handle errors/exceptions in IO? By throwing an exception. That the program can either handle, or let crash. Like every other exception. Once again, if you can handle the IO exception in some meaningful way, then you can do it with a try-catch block or whatever that language uses. But you dont’ have to. That’s the difference.

Downside 4: No Built-in Null Safety

Java also does not have built in null-safety, whereas Kotlin, Rust, and Swift do. This means many functions have to check a null input for any non-primitive datatype. This adds several repetitive lines of code for null checks throughout the system.

This can be someone alleviated by design. Common rules include “Don’t Return Null” and “Don’t Pass Null”. However, when developing your own APIs or working with APIs written by others, you will often still be forced to contend with null inputs and outputs, at least at the boundary between your code and theirs.


Conclusion

The language is the vehicle, not the road. Most of the concepts you learn in this class are applicable to any object-oriented programming language, and most popular modern programming languages are object-oriented. This course doesn’t exist to make you a better Java Programmer. It exists to make you a better software developer, designer, and planner.


Next submodule: