This post is directed at Python programmers (in particular, data scientists) traveling in the opposite lane; I’ll detail some of the initial conceptual hurdles you may face when learning to adapt your Python instincts to Java territory. In particular, I’m going to be avoiding superficial differences like snake_case
vs camelCase
or how Java requires you to end a statement with a semicolon while Python makes it optional. I’m also going to avoid diving too deeply into OOP vs. functional programming. The focus of this post is the way in which Java requires you to think differently about how to solve whatever problem you’re working on.
Although it may seem daunting, just remember that many programmers before you have successfully learned Java, so it absolutely can be done.
1. Java has more overhead than you’re used to.
print('Oh hi there, globe')
To accomplish this in Java, you need to create an entire class, complete with a main
method to serve as the entry point.
public class Room {
public static void main(String[] args) {
System.out.println("Oh hi there, globe");
}
}
main
method working with variables and telling other classes what to do (and sometimes, that main
method is hidden deep within automated files, like with Android projects). Although there are some ways to run Java outside of a complete class––I was a big fan of the interactions
tab in jgrasp while teaching––there are some hoops to jump through to make this happen. Room
above: System.out.println()
. This seems like way more work than it should be just to produce console output, right? That’s because we’re being very, very particular about how we want our output to be displayed; out of all of the different places we could put that text, we want it to go to the console, as opposed to a log file, a pop-up window, or a server off in the ether. Python, by default assumes that by print
you want the text displayed on the console, but Java does not generally make assumptions like that. 2. Java requires you to be way more specific than Python.
that_value_i_need = some_object.mystery_method()
only to have to follow it up with:
type(that_value_i_need)
mystery_method()
is returning. mystery_method()
, so it’s definitely possible that you already know what you’re getting before you call it. But Python lets you get away with this. You can create a variable for that_value_i_need
and know that it can handle whatever mystery_method()
spits out. important_value = some_method(a, bunch, of, arguments)
important_value = unrelated_class.another_method()
important_value
is just a variable, which can hold any data type it needs to… right? x
is an int
, because it was declared as an int
on line 72. If it’s holding a value returned by someUsefulMethod
, that tells mesomeUsefulMethod
returns an int
(or something that can be promoted to an int
, like a short
). error: cannot find symbol
messages when you try to run yours, the fact that Java requires you to be so specific leads to code being significantly more self-documenting, predictable, and unambiguous than Python. And although I need to be way more meticulous when writing my Java code, I feel significantly more confident in understanding how it works. javadoc
is a powerful tool for generating clean documentation––even if you haven’t explicitly written comments (which you should). javadoc
will read your code and generate organized html files indicating exactly what data types you need to pass a method and what value you can get in return. Most IDEs even have a button that will do this for you. 3. Java is more strict than Python.
# version 1 - single quotes
print('Oh hi there, globe')# version 2 - double quotes
print("Oh hi there, globe")# version 3 - triple single quotes
print('''Oh hi there, globe''')# version 4 - uh, triple double quotes, because why not?
print("""Oh hi there, globe""")
String
s is significantly more straightforward: Always enclose them in double quotes. If you have a double quote inside your String
, put an escape character
before it: ”
. If you have a
inside your String
, put an escape character before it: \
. If you ever see single quotes, like with 'a'
, that means you’re dealing with a char
, not a String
. int
to a KitchenSink
––into a list in Python, you’ll find that working with Java arrays require a very different mindset. All elements in a Java array have to have the same data type, similar to an ndarray
or a Series
, but with careful design, you can get around this limitation with polymorphic references. global
variables available to all class methods in Java, whereas in Python you might accidentally wind up using a syntactically valid (yet perfectly inappropriate) value from a completely different part of your program if you ever recycle a variable name. On top of that, you can actually have private
fields in your class, instead of hoping everyone respects your underscored variable name. 4. Java is compiled, so many errors resulting from misunderstanding the previous points are caught before runtime.
SyntaxError
if it can’t figure out what you’re going for, but I’m sure all of you (especially data scientists) have been annoyed at one point or another when your code screeches to a halt over a simple error, after accomplishing half of what you wanted it to do. Oftentimes (in my experience, anyway), this happens because you’ve attempted to perform some sort of an operation — say, calling a method — on an object of the wrong type. It’s not a syntax error (you remembered the .
!), so the parser doesn’t catch it, and the traceback takes you down a rabbit hole into the core libraries, so you can see exactly what Python assumed you wanted to do with the mystery object you gave it.
NullPointerException
and ArrayIndexOutOfBoundsException
––but this added layer of verification, combined with the strict and specific nature of Java, tends to funnel these runtime errors down predictable alleys. 5. Designing Java methods is almost nothing like designing Python functions, so you need to think about your problem differently.
A downside to this strictness is that Java method calls can become unruly pretty quickly. I absolutely love the functional side of Python, and have started relying not only on unordered keyword arguments, but also passing functions and having default values for optional parameters.
This is totally valid Python code…
def multiply(a, b, c=1):
return a * b * cmultiply('hello', 3)
…which would not fly in Java. Because Java is static-typed, you lock in the data type when you declare the variable. This means you have to pass values that have exactly that data type in exactly the same order as they appear in the method signature. While this reduces flexibility, it also reduces misuse of your code.
While you can have methods with the same name handle different lists of parameters, Java handles this via overloading, which requires you to write a different version of the method to handle each sequence of data types you want to support as a parameter list. Java checks the data types you passed, sees if there is a method defined for handling that sequence of data types, and calls that method––methods with the same name and different parameters aren’t really linked in any other way.
And if you want to provide a default value for one of your parameters? You have to overload that method and drop the default-value parameter from the list.
The fact that signatures are defined by the data types in the parameter list instead of the name of the variable also creates issues when you’re used to thinking in Python. For example, if we wanted to provide default arguments for overloaded versions of this method:
public static double getArea(double length, double width){
return length * width;
}
We would run into an issue by including two versions with the same data types in their parameter list:
public static double getArea(double length) {
return length * 10;
}public static double getArea(double width) {
return width * 5;
}
Both of these methods are called getArea
and both expect a double
, so when you call getArea(12.3);
, Java does not know which path to go down.
In addition to that, while Java does have some support for lambda expressions, they’re a far cry from functional programming, and attempting to think through a functional solution in an object-oriented language is begging for disaster.
Oh, and return types? Java limits you to one––and you have to specify the data type when you write the method, which requires you to know what you want to return well in advance. You may be used to thinking about Python as being able to return multiple values…
def get_stuff():
return 1, 2, 3x, y, z = get_stuff()
…but it actually returns a single object––all of the values bundled into a tuple:
>>> type(get_stuff())
<class 'tuple'>
You can be surprisingly productive in Python without ever knowing this fact, but in Java you always have to know what your methods are returning.
6. In addition to data types, Java requires you think about lower-level concepts like memory management.
This one might be a bit of a hot take (especially for people coming from a language like C that don’t have automatic garbage collection), but there are certain pieces of Java that require you to think about what’s going on, memory-wise. I’m talking about reference variables!
For example, what happens when you try to run the following code in Python?
my_list = [1, 2, 3]
print(my_list)
If you’re coming from a totally-Python background, you’ll expect the following output:
[1, 2, 3]
On the flip side, what do you get from the following Java code?
public class JavaFeatures {
public static void main(String[] args) {
int[] myList = {1, 2, 3};
System.out.println(myList);
}
}
That’s right! It will print the memory address. Here’s where my computer stored that:[I@6d06d69c
Again, Python makes the assumption that when you print a variable referencing a list, you want to see the list’s contents, while Java, by default, prints exactly what is stored in that reference variable: the location of the array in memory. To display the contents, you would need to loop through them:
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i]);
}
While this takes a little bit of effort to wrap your brain around, it forces you to think about what’s actually being passed when you throw around reference variables (the memory address of that object), which gives you a deeper understanding of how your computer is actually carrying out its tasks.
While Java is not as low-level as other languages, it’s much easier to make the leap from Java reference variables to C pointers than it is to leap from Python references, because you’re used to encountering problems like the one above that force you to consider the memory address.
But the lack of Python’s “set it and forget it” mentality isn’t limited to reference variables.
7. Java doesn’t really have a great equivalent for <code>pip</code> or <code>conda</code>.
When I think about what makes Python a useful language, I don’t generally think about language features or syntax. I think about the Python ecosystem. With a quick trip to the command line, you can use something like
pip install snazzy_new_library
to download a snazzy_new_library
for use whenever you need it.
Although you will find people on message who will insist that package tools like maven
or gradle
are just as powerful as pip
or conda
, the reality is that while these tools are, indeed, powerful, they aren’t really equivalent. Java programs are generally structured differently––rather than making a package globally available to any program in that environment, packages tend to be bundled directly with the application that needs them, and often need to be either added manually by dropping a jar file into the right place, or included with a tool that manages your dependences (which is what tools likemaven
and gradle
are actually great at).
This can be inconvenient for Python programmers, because it requires you to jump through additional hoops to include a useful existing library on your project, and the size of the project files can get bloated as a result. On the other hand, bundling the included library with the project ensures that the necessary code is actually available when it’s needed.
Conclusion: You can learn Java, and it will make you a better programmer.
Having to adapt your problem-solving skills to fit the quirks of a new programming language can be a scary task, but picking up a new language really does expand the arsenal of tools in your tool kit––even in your first language. C pointers confused me until I got used to working with Java reference variables, and lambda expressions in Java baffled me until I saw how they were used in functional programming with Python… which in turn wraps back around to clarifying why we need function pointers in C.
Although it may seem daunting, just remember that many programmers before you have successfully learned Java, so it absolutely can be done.