Java for Rubyists

The Scout Java Application Monitoring Agent is under active development and we have a few spots open in our alpha program. Email support@scoutapm.com for access.

For many Rubyists, Ruby is the first language that they learn and perhaps the only programming language that they know. Ruby provides an excellent gateway into the world of programming by allowing the newcomer to pick up its syntax rather quickly and allowing them to produce a working application within hours or even minutes using frameworks such as Rails.

Regardless of how one came to learn Ruby, I believe that programmers can benefit from learning another language that is fundamentally different, such as Java.

I'll provide a brief introduction into the world of Java from the perspective of a Rubyist. For the sake of staying focused, I'm not going to cover the difference in syntax between the two languages as this is best picked up over time, nor am I going to get into the details of more advanced Java topics such as the Java memory model or concurrency. What I do want to do is give a brief overview of the language, and why I think learning Java can benefit a Ruby programmer. Finally, I plan to cover some popular Java tools and frameworks and how they correspond to those in the Ruby world.

Why?

Perhaps one of the best places to start when discussing a Rubyist learning a language such as Java is why?. Why would a perfectly good Ruby programmer want to learn an 'enterprise' and 'heavy-handed' programming language such as Java?

Well, I believe there are multiple reasons:

  1. Versatility. Learning a different language, especially one that is different not just in syntax, allows the programmer also to think about and approach problems differently. When approaching a problem in Ruby, I frequently ask myself 'How would I solve this if I were coding in Java?' and vice-versa.

  2. Marketability. Frankly, there are only so many Ruby jobs available and learning a second language such as Java can increase one's value in the job market. According to a 2015 study, Java was featured in 18% of programming job advertisements.

  3. Performance. Processor intensive applications can only perform so well when written in an interpreted language such as Ruby. Java can significantly out-perform Ruby in cases where performance is processor bound. Not to mention the Java JVM can optimize Java code even further and can sometimes compile to native machine code using the JIT just-in-time compiler.

  4. Popularity. This is no longer the Java of times past. Java has recently seen a resurgence of interest according to Github and has no signs of slowing down.

Convinced that Java is worth looking at? Let's dive in.

Brief History

Java was originally developed at Sun Microsystem in 1995 by James Gosling and team. Designed as a general purpose object-oriented programming language with support for concurrency built in, Java is described as being a 'write once, run anywhere' language. Compiled Java bytecode can run on any platform with a Java Virtual Machine (JVM) installed.

Similarly, Ruby was also developed in 1995 by Yukihiro Matsumoto to support multiple programming styles including functional, objected-oriented and imperative. Ruby was designed with the programmer in mind by focussing on 'programmer happiness' over needs of the machine.

Interpretation and Compilation

Note: The implementation of Ruby referenced in this post is Ruby MRI.

Ruby is usually thought of as an interpreted language. Each time a Ruby program runs, the Ruby runtime breaks apart your code into tokens (tokenizes), parses those tokens (groups them into meaningful Ruby statements) and compiles those statements into low-level instructions. Since Ruby 1.9, Ruby code is compiled into bytecode that runs on the Ruby VM known as Yet Another Ruby Virtual Machine (YARV), much like how Java bytecode runs on the JVM.

Unlike the JVM however, Ruby never compiles your code all the way to machine language. Ruby bytecode is still interpreted by the YARV each time it runs. Also, the Ruby compilation process is hidden from the programmer and is not exposed as an external tool like the Java compiler is.

Before a Java program runs, it is compiled into Java bytecode by the Java compiler as a .class file. Like Ruby, Java bytecode is executed by the virtual machine (JVM) and is platform independent (Java bytecode can run on any OS that can run the JVM). Modern JVMs, however, can use a technique called just-in-time (JIT) compilation to compile Java bytecode into native machine code at runtime. The JVM can do this for 'hot code', or code that is run thousands of times during the execution of the program to increase performance.

Dynamic vs. Static Typing

One area where Ruby and Java drastically differ is their type systems. Ruby is dynamically typed, or as many Ruby programmers refer to it: 'duck-typed', in that if it 'walks like a duck and talks like a duck, then it's probably a duck'. In Ruby, an object's suitability to process a message (or call) is determined by the presence of its methods and/or properties.

Java, by contrast, is statically typed and type checked at compile time. When a Java program is compiled, the Java compiler checks each object to ensure that it can handle a message or function call that it receives. It does this by enforcing that every Java object conforms to a pre-defined type.

More easily demonstrated by example:

Ruby

Let's imagine we have an existing class Dog:

# dog.rb
...
dog = Dog.new
puts dog.woof # "woof!"
puts dog.meow

Running the program with Ruby:

$ ruby dog.rb

Gives us the following output:

Woof!
NoMethodError: undefined method 'meow' for #< Dog:0x007fe56105c3c0>
        from (irb):4
        from /Users/mark/.rubies/ruby-2.3.0/bin/irb:11:in '< main>'

The Ruby program above will run just fine until the last line is reached. Ruby will raise a NoMethodError at runtime since the Dog class does not implement meow.

Java

Here's a similar implementation in Java:

public static void main(String[] args) {
    Dog dog = new Dog();
    dog.woof();
    dog.meow();
}

Let's compile the program first using javac:

$ javac src/Dog.java
src/Dog:16: error: cannot find symbol
        dog.meow();
           ^
  symbol:   method meow()
  location: variable dog of type Dog
1 error

Java won't even compile our code because it knows that the Dog class does not have a method meow, so this code could not possibly run successfully.

While it can be seen as annoying and overly verbose by some to have to declare the type of an object before using it, sometimes the benefit of type safety can outweigh the cost. Having the ability to catch typing errors at compile time can be very beneficial when it comes to writing bug-free production ready code.

Tooling

One benefit that Java has over Ruby is the amount of tools and frameworks built around the language. Java's popularity with larger enterprise companies has helped it evolve by having many smart people contribute to the language itself and also to the tools that developers use every day.

Let's run down the list of some of the more popular Java frameworks and libraries:

Web Frameworks (Rails, Sinatra, etc.)

Web Servers (Unicorn, Puma, etc.)

Testing (RSpec, Minitest)

Dependency Resolution (Bundler)

IDEs

Performance Profilers

The Scout Java Application Monitoring Agent is under active development and we have a few spots open in our alpha program. Email support@scoutapm.com for access.

For many more frameworks and tools, check out the Awesome Java and Awesome Java 8 lists on Github.

Summary

Many programmers who have migrated to Ruby from other 'enterprise' languages such as Java or C#, embrace Ruby's expressiveness and focus on 'programmer happiness'. While it is true that Java is more verbose in many cases when compared to Ruby's terse syntax, I believe that modern versions of Java, such as Java 8, have made many strides in closing the gaps.

Java now has built-in support for lambdas and functional programming and has improved several of the outdated APIs such as its Date API. Additional libraries such as Google Guava and Apache Commons also help extend the Java Core API by adding easier to work with APIs for collections, concurrency, I/O processing and much more.

Finally, a Ruby programmer looking to learn Java may also do so gradually by working with other more Ruby-like languages that run on the JVM such as Groovy, or by playing around with JRuby.

We have barely uncovered the tip of the iceberg when it comes to what Java has to offer, as there is way too much ground to cover than could fit in a single or even series of blog posts. However, I do hope this post has inspired you to give Java another look and perhaps even start looking at beginning to add it to your programming toolbox.

Subscribe for more

Want more Ruby-related performance insights like this delivered monthly to your inbox? Just put your email into the sidebar form.


Mark Phelps is a Senior Software Engineer and Team Lead at Validic in Durham, NC.

He loves writing clean code and building great software, mostly in Ruby and Java. He also writes about software and startups on his blog.

He graduated in 2008 with a B.S. in Computer Science from Old Dominion University.

When he's not busy writing software, he can usually be found at home in Durham, NC drinking coffee or a good beer with his wife and two dogs (the dogs mostly drink water though).