In case you didn't notice there is a trend for developing new languages that run on top of JVM. Jython, JRuby, Scala, Groovy, Clojure are examples. It seems latest addition will be the RedHat's Ceylon.
After 6 years of professional experience with Java, here is what I would do to fix it and what I wont touch.
Don't touch the packages.
Packages are convenient and simple ways to define the namespace. So I wont touch the way it is. You have to define the package keyword for every class/file and specify imports below that. I would farther force the constraint and remove the default package.
Improve the imports with alias.
Only problem I had with imports are on the rare case that, when I want to import more than one class with the same name. Alias's could be introduce to improve that. Here is how I would have used them ;
import org.apache.commons.lang.StringUtils as CommonsStringUtils;
import org.springframework.util.StringUtils as SpringStringUtils;
...
if(CommonsStringUtils.isEmpty(str)) {
...
One more rule I would have on defining an alias is that you can't define alias if there is not any naming collision. This would prevent the abuse of use.
One public class per file.This is a rule that most of the languages remove, which I object. I like it and won't touch it. With the packaging rules and this, I can find the source code of the class easily.
Access modifiers.
Remove the private keyword from language and make the default visibility private for classes and variables. If needed introduce a separate keyword for package level visibility.
No headless files.
Another common trade of the new languages are letting the users code without any class definition. Like a single lined print statement without any class definition is a valid program. I don't think enabling this freely would encourage anything more than some garbage scripts.
Improve Getters Setters, Simplify Initializing Constructors
Java Beans spec. dictates that a bean should define private fields and make these fields accessible through what we call getter/setter methods. This is widely used in today's apps. and frameworks. Hibernate and other JPA frameworks use it extensibly.
I believe getter/setter methods are mostly useless and clutters the code.
Again another source for boilerplate code is the initializers.
I think best way to solve this is by introducing new meta information.
public class City {
public get String name; // public getter no setter
public get set Integer population; // public getter - setter
public get protected set Integer foo; // public getter - protected setter
}
...
tokyo.getName(); // to use
So public or package level visibility for a field must be used with get/set keywords. Specifically defining the get/set methods will override the default methods. And at last I believe that setters should return 'this'.
For the constructors I don't have any solution that I like for now. Groovy and some others might too, have a map syntax for constructors.
No Primitives
Java has distinction between 1L and the Java Long object. They should be same. Constructors for Long object must be removed and typing 1L should create the Long object. Again method calls could be made like 1L.toString()
Null checks
Again a common source for boilerplate code. I like the Groovy solution :
if(state != null &&
state.getTransitions() != null &&
state.getTransitions().size() > 0) {
states = state.getTransitions().values();
} else {
states = new ArrayList();
}
Could be written as :
states = state?.transitions?.values() ?: new ArrayList();
? is the null check and ?: is called the elvis operator.
Closures
Another popular construct that people try to add is the closures. Closures are simply function blocks that could be passed as parameters to methods or other functions. This is what closures look like in Java :
jdbcTemplate.doInConnection(new ConnectionClosure<Long>() {
@Override
public Long execute(Connection con) throws Exception {
ResultSet set = con.createStatement().executeQuery("select max(id) from cacheoperations");
while (set.next()) {
return new Long(set.getLong(1));
}
return new Long(0);
}
});
That is the OO way to define a closure. I am sure that on many enterprise apps. some similar code for accessing a Connection object exists. We don't want unclosed connections so we employ this pattern to be sure that connection is closed after the closure is done. While this works it's cumbersome to define. Every closure needs an interface with just one method to be defined. In this case the
ConnectionClosure interface. Improved version with no interface might look like this :
public class JDBCTemplate {
public Object doInConnection(Object execute(Connection con)) {
...
// use
jdbcTemplate.doInConnection(
Long execute(Connection con) throws Exception {
ResultSet set = con.createStatement().executeQuery("select max(id) from cacheoperations");
while (set.next()) {
return new Long(set.getLong(1));
}
return new Long(0);
});
To improve code use any object containing a method with the mentioned signature could be passed as argument to this function.
What needs to be thought further is how this would effect the Java api design.
Biggest Don'ts
Don't change the Java's basic syntax. Don't add pascal like begin and end keywords for blocks. Curly braces are enough. Same thing for loops and if statements. I don't think there is a need for introducing a new assignment operator like :=.