David Pollack founded Visi.Pro, Cloud Computing for the Rest of Us along with the Visi Language open source project. David founded the Lift Web Framework and continuously contributes to Lift. David is a DZone MVB and is not an employee of DZone and has posted 28 posts at DZone. You can read more from them at their website. View Full User Profile

Java: a Local Minimum Language-wise

10.26.2013
| 2039 views |
  • submit to reddit

I wrote hundreds of thousands of lines of code in Java between 1996 and 2002. I wrote web frameworks, spreadsheets, and much much more in Java 1.0 through Java 1.4.

Compared to mid-90's (pre-templates) C++, Java was a totally amazing language. And the JVM is the best run-time for any computer language.

The Long Strange Trip

In 2002, I started doing C#. Then Ruby. Then Scala. Some Haskell and most recently, I've been doing a lot of Clojure.

So, the amount of Java 1.5/Generics I did up through this year is very limited.

Got a Java Gig

This year, I got a gig writing some very interesting code (writing a compiler). But, the client is a Java shop. The project already had a Java base. And the client was adamant that the project be done all in Java... not Scala... not Clojure... not JRuby, but Java.

So, I've spent the last 6 months writing thousands of lines of Java code. I've seen how Java has evolved over the last 10 years and it's shocking. Shocking how bad Java has become.

Basically, Java is pointlessly verbose. Generics add, as far as I can tell, about 20%-30% to the overall character count in a given program. While Scala's types, with type inference, are a net positive vs. Ruby (the jury is out in my mind about the weight of Scala's type system versus its value against the less typed Clojure.) Java's amazing verbose, visually distracting, ugly as anything I can think of Generics are not worth the marginal improvement in type checking vs. Java 1.4.

So, I'm going to go through what I think could make Java better without breaking the language.

This Is Is Not a "Scala Is Better Post"

Note that this post is not a "Scala is better" post. While some of the ideas in the post come from Scala, I do not see Scala (or Clojure) as a reasonable replacement for Java. Scala is just too much of a language and I've seen a ton of Scala mis-use.

Further, I think Scala has to a great degree suffered some of the same growing pains that Java did (adding weight and complexity to the language [mostly via the type system for Scala] to address marginal issues), which is not surprising given both languages' lineage.

Some of This Stuff May Be Proposed or in JDK 8

These are my thoughts. I'm not a player in JCP-land. I know about lambdas in Java 8 thanks to an excellent lunch with Sam Pullara. But I'm not up on a lot of JCP traffic, so these thoughts may be covered by JCP proposals.

Default Public

Default all declarations to public unless there's an explicit visibility level. Yes, I understand this breaks some existing code, but let's just make the default thing the right thing.

Why? Because the fewer pieces of noise we have as part of our code, the more the signal will stand out.

Last Expression Is the Return Value

Another place where the signal becomes more valuable is no longer requiring the return keyword. The last expression becomes the return value:

int plusOne(int x) {x + 1;}

This is only applicable if there are no return statements in the method. So, if you're going to usereturn for flow-of-control programming, you must be explicit. But for small, simple methods, getting rid of return means less visual noise.

Block Expressions

Anything in curly braces is a block expression, rather than a block statement. This means that if curly-brace-thing could be an expression, it's treated as such.

String s = {
var d = new Date();
"The current date is "+d;
}
Some Type Inferencing

I would like to see automatic assignment inference. Basically:

var name = "David"; // it's a String, duh

We do not need to make var a keyword. Basically, the type var becomes the "infer the type" type.

And basic left-hand-side parameter type inferencing (note the IntelliJ already uses this syntax):

 List<String> stuff = new ArrayList<~>();

And return type inferencing if there's a single path to the return value (no return statements except the last line of the method):

 plusOne(int x) // it's an int
{
x + 1;
}

All of the above changes can be done with data already known to the compiler. There's no fancy algorithms needed. It's just the reduction of repeated words in the program... and with the Generics type inferencing, it's what IDEs already display to the coder anyway.

Better Equality Testing

Add === as an alias to the equals method, but require the righthand side of the === be co-variant with the righthand side.

Also, add !== as !equals.

Simple, Immutable Data Structures, with metadata

Representing simple data in Java is a huge pain. Yes, the IDEs help out a lot in terms of creating getters/setter. But at the end of the day, a lot of our programs contain raw data. So, I propose adding inner data structures to Java.

These are a combination of Scala's case classes with Clojure's metadata facility thrown in.

structs can only be inside a top-level public class.

For example:

public class MyStuff {
struct Name(firstName = "David", lastName = "Pollak", 
age = 49, Date birthday = null)




public static boolean funWithNames() {
var archer = Name("Archer").age(10);
var tessa = Name().firstName("Tessa").lastName("kitten").
age(1);




assert(tessa.age === 1);
assert(tessa.getAge() === 1);




archer !== tessa;
}




public static void metaData() {
var david = Name("David", "Pollak");
Name d2 = david.setMeta("geek", "true");
assert(david === d2); // metadata not part of equality testing
assert(david != d2); // object instances not the same
assert(david.getMeta("geek") === null);
assert(d2.getMeta("geek") === "true");




}
}

What do you get with a struct?

  • Immutable object creation without new and with positional constructors for each parameter as well as simply, nominal chaining of creation and new values.
  • Map<String, String> of metadata that's associated with each struct.
  • Default values for each of the fields. This allows the addition of fields, and new versions of a library with additional parameters will not break code accessing old versions.
  • toString method that prints the field names and values.
  • A correctly implemented equals method that does all field comparison with proper treating of null fields.
  • hashCode method that computes the hash code (this should be cached if all of the fields are known to be immutable).
  • Field level accessor methods.
  • Per-field copiers: field(param) This allows the creation of a new instance with a new value. The metadata is copied.
  • Lots of metadata is available for each struct including the list of field names, the type of each field. This metadata could be used for a fast, efficient, automatically generated set of serializers/deserializers (e.g., JSON, JDBC, XML).

Also, please note the syntax for the definition. The type is inferred if it can be.

For each class that contains a struct, there will be a ClassName.struct interface. The interface is implemented by all the structs and the interface contains all of the shared fields. For example:

class People {
struct Parent(String name = null, List<People.struct> kids = null)
struct Kid(String name = null, Parent parent = null)




String getName(struct person) {person.name;}
}

There are a bunch of things that can also be done with annotations (e.g., enforcing the JSON serializability of a struct, etc.)

But having lightweight way to define a data pattern at the language level makes code faster to write, easier to maintain and more readable.

Smarter Casting

Java casting is too verbose and error prone. Using the if/instanceof/cast pattern takes a lot of code and is very error prone because the class is specified twice... once in the test and once in the cast.

We can borrow one of my favorite features from Scala: pattern matching, but doing it simply:

String testIt(Object o) {
switch o {
String s: s;
Number n: "It's a number: "+n;
Date d if d.millis() > 0: {
var formattedDate = formatDate(d);
"It's a modern date: "+formattedDate;
}
default: "No clue";
}
}

Okay... what do we have? The class is tested. If the class is a match, then assign it to the variable (which only has the scope of the line). Next, run the guard to see if the case should be taken. If so, run the expression to the right of the :.

Note that this form of switch is an expression rather than a statement. Also, note that there is no fall-through.

So, what else does this make easier?

String whatSize(int i) {
switch i {
v if s < 10 : "small";
v if s < 100 : "medium";
default: "large";
}
}

The next thing we can do is apply it to structs (no, this is not pattern matching):

String aboutPerson(People.struct p) {
switch p all {
Parent p: "A parent named "+p.name+" #"+p.kids.size();
Kid k: "A kid with "+(k.parent.kids.size() - 1)+" sibs";
}
}

The all keyword after the express means that there must be a match. This is useful if one adds a new struct to a class because all the places where there's switching on the struct will flag a compile-time error.

Immutable Collections

Java desperately needs an excellent immutable collection package.

Basically, the package would be a pure rip-off of Clojure's excellent Map and Vector classes. We don't need a lot of fancy Scala-like collections.

Immutable collections that do not implement java.util.List or java.util.Map . Basically, these collections would be stand-alone.

Combined with JDK 8 lambdas, I'd get a whole ton of what I love from Scala and Clojure in terms of collection manipulation.

So, There You Have It

So... there you have it. I think the above would make Java a much easier to deal with language. It would reduce verbosity. It would allow dealing with data as data which is fairly common, especially when writing services that marshal data in and out of systems.

The above changes would reduce Java's verbosity without significantly detracting from Java's "it tells a story" code. And, yes, it's possible to write fairly impenetrable Scala code. We don't want to go where Scala went.

What else would I like to see? Call-by-name parameters (I don't know if they'll be part of lambdas). Properties. It would also be interesting to see if it'd be possible to have some form of global type inferencing in Java. But all that stuff is for another day.

Rock on!

Published at DZone with permission of David Pollak, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Jean-Baptiste Nizet replied on Sat, 2013/10/26 - 6:21am

 Here's what I think about your suggestions:

Default Public

It should be default private instead. Let's promote encapsulation.

Last Expression Is the Return Value

Don't like it. It makes code modification harder, and makes the code less coherent. What is returned is not as abvious when reading the code (and we read code much more than we write it). Add an if block and suddenly you have to add a return. 

Block Expressions

Why not make the block a private method instead? It would make the code more readable, and would promote non-duplication and short methods doing one thing instead of long methods assembled from unrelated code blocks.

Some Type Inferencing

I don't really understand what you gain with that. What you lose is obvious though: you have to transform yourself into a compiler to know the type of the variable rather than simply reading it. It obfuscates the code.

List<String> list = new ArrayList<~>();

What do you gain over List<String> list = new ArrayList<>(); which you can use since Java 7, out for more than 2 years already?

Better Equality Testing

Not too bad, but with 2 problems:

- the === operator means reference equality in other languages. That would be quite confusing to adopt the inverse convention

- it would lead to harder to understand NullPointerExceptions in case the first operand is null (just like auto-unboxing).

Simple, Immutable Data Structures, with metadata

I agree that data structure objects are a bit painful in Java. You might look into Groovy, which has shortcuts to define such classes, and is fully interoperable with Java.

Smarter Casting

Needing that or pattern matching, IMHO, is just a smell of poor design. Why not use a common ancestor class or interface and polymorphism? That's what OO is all about. Using instanceof makes the code non-typesafe, and is a sign that polymorphism should be introduced, most of the time. There are of course valid use-cases for instanceof, but they're quite rare.

Immutable collections

The excellent Guava library has immutable collections. They do implement the standard Java collections though, which IMO is a good thing.

Jean Said replied on Sat, 2013/10/26 - 6:55am

I mostly disagree with your ideas. Java is verbose by James Gosling's own design. It intends to be verbose. Why? Becausee he wanted to reduce "language magic".

Public by default is the perfect knife to impale yourself on. Making it private by default makes the whole thing saner.


What would really make my day is integrating some Project Lombok  annotations into the JDK. @Setter, @Getter (or even better, @Set and @Get), @ToString, @EqualsAndHashCode. Boilerplate setters, getters, toString, equals and hashcode, make many classes look totally ridiculous. Even a reasonably low coupled class gets from 25% to 40% of this crap.

A simple javabean, you know, the ones you get everywhere in your code, are mostly in the 70% to 80% of useless crappy and obscurcing boilerplatecode That is insane.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.