Java Primitive Types are bad for Learners
This year I am teaching Java to first semester students. My own Java introduction has happened seven years ago. As I am quite interested in principles of programming languages I try to convey my enthusiasm for the topic and base my didactic methods around that. It's cool what we do. Let's talk about it.
I see the problems in a lecture for first semester students as they have yet to learn any theoretical basis for object oriented programming, that would be useful. In my oppinion one should start with the theoretical basis, like class hierarchies and the object model. But students only learn about partial orders (like a class hierarchy) later in the first year. So ideally (in my mind at least), a computer science education is without a computer for the first semester.
And because there is no theoretical basis, the approach taken by the professor is quite practical, and just barely supported by theory (wherever possible, but that's not much). This means that after the first lesson, students already know what a Hello World program looks like and they can fiddle with it. Even though they do not know what a class is, a static method, even methods or functions at all.
This leads to further problems down the road. We have an online platform where students have to solve programming assignments during seminars and as homework. But since they don't know about classes and class methods (or types or return statements for that matter), we can't have them implement classes, methods, or interfaces as homework and have to have them write output into the main method. That's a bad start, when you consider programming best practices.
Ideally one would like to start implementing small methods without function calls that implement some interface and then go on in creating more complex programs later on. But the problem is, that there is no way to run the java code without a main method. And even if you provide one, then the students can not really practice.
This shows how necessary a REPL or interactive interpreter is.
But Java has a few more problems that are quite bad for learners and can lead to confusion and misunderstanding.
Some Types are Keywords
For legacy reasons, Java has two kinds of types. There are primitive types that correspond in some manner to machine types, and the reference types that form the class hierarchy. Examples of primitive types would be int or float or void (Unit - only allowed as method return type). In general it's also the case that operators work on primite types but do not for reference types (exceptions apply). Those primitive types are builtin insofar that their type names are keywords as well.
As a homework for the second week I gave the following task: write a program that reads a number between 0 and 100 from the standard input (they don't know about standard input yet, and I'd prefer passing arguments as command line parameters but was overruled) and write a 10 character progress bar, visualizing the progress that correspond to the input number. This is my solution I worked from:
import java.util.Scanner;
class Progress {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int progress = input.nextInt() / 10;
int rest = 10 - progress;
while (progress > 0) {
System.out.print("|");
progress = progress - 1;
}
while (rest > 0) {
System.out.print("-");
rest = rest - 1;
}
}
}
Since this is quite complicated for beginners, I've given them the basic structure and removed all keywords, replacing them with % and replacing all variable names with #. Resulting in this:
import java.util.Scanner;
% Progress {
% % % main(String[] #) {
Scanner # = % Scanner(System.in);
% # = #.nextInt() / 10;
% # = 10 - #;
% (# > 0) {
System.out.print("|");
# = # - 1;
}
% (# > 0) {
System.out.print("-");
# = # - 1;
}
}
}
And as you can see, not entirely all type information is removed by replacing the keywords with #. If all types were reference types, we'd have a program that looked more like this:
% Progress {
% % Unit main(String[] #) {
Scanner # = % Scanner(System.in);
Integer # = #.nextInt() / 10;
Integer # = 10 - #;
% (# > 0) {
System.out.print("|");
# = # - 1;
}
% (# > 0) {
System.out.print("-");
# = # - 1;
}
}
}
Now doesn't this look quite a bit better? Some types are introduced as keywords but all user-created ones are not (and by user-created, I also mean the standard library). This is an inconsistency that is quite hard to explain away, even more so if you have yet to introduce the students to the vocabulary to explain this.
There are even more problems quite soon. Once conditionals are introduced, we have the next problem. The language construct if () then {} else {} only works with the bool type as conditionals. So primitive types are necessary to use the language constructs for conditional execution (while as well, but the numeric comparison follows somewhat naturally).
I do not know whether we will get this far, but for type parameters (for for polymorphic types, like containers) the primitive types don't work either.
All in all, I'd say Java would need to do away with primitive types and consistently use reference types. But for this to work properly with the operators, one would probably also need to add operator overloading. Once you're there, you could think long and hard about whether you'd really need the new keyword for instance construction.
And of course, one would want to introducing a proper Unit type. But since in Java all types are inhabited by the value null (who the hell had that idea?), this would be useless. So I will allow keeping the void keyword.