For the love of Java
My old blog is lost, I think I have a mysql DB on a SSD disk on its last leg. Anyway the old Ed, the old blog, might go on a rant. These days I try to take the high road. I say try, I'm not all the way there. A few weeks back I found a linked in post that was discussing Java 23 added "var" and that it was "too little too late".
In case you didn't know the story, I was born to be a Java fanboy. When the applets made 3d shapes in my browser, I was in love. This was a long time ago, I had windows 3.11 and you needed a 32 bit OS to run Java, upgrades were required. My Packard bell PC only had 8MB RAM, upgrades were required.
Everyone is free to like and dislike what they want. I'm going to say my piece about "too little too late". Over the years Java improved drastically, both in terms of performance and features. Even features I didn't like or understand initially, I now use like second nature. Most impressive to me is the 'old way' stills works. I the developer didn't have to go though breaking changes as the language evolved (and if Im a crumudgen I can still code it like 20 years ago).
I cant recap 20 years of changes in one blog, but Ill show a few that really powerful. The code is here on github.
Generics
Before generics Java had collection classes and you had to cast objects on the way out:
class Something{}
@Test
void javaOldDays(){
List x = new ArrayList();
x.add(new Something());
Something s = (Something) x.get(0);
}
Java objects have pointers to their "class", allowing things like "if (x instanceof Integer)". Generics can be used many ways, but one of the easiest to grock and most useful is removing the cast above.
@Test
void javaNewDays(){
List<Something> x = new ArrayList<>();
x.add(new Something());
Something s = x.get(0);
}
Did you notice <Something> and <>? Now, I will tell you again I am taking the high road, when you find them Java haters, they may start in on how c++ templates use <> and are "far superior" for reason x, y, or z.
This is really like night and day stuff, they look the same <> but are very different, the c++ templates are a compile time thing, where Java generics are closer to runtime "type erasure". This is just a fundamental difference in java, the VM is knowing the class of every instance. Things like "reflection" allow you to introspect, while in your c/c++ world the runtime information/introspection might not be enabled. Generics can do much much beyond tagging container/collection objects, whenever I used them I alternative between thinking it is the best design ever or over designed. Maybe a future blog Ill bring more examples.
Try with resources
While everyone loves "functional programming", if a language was purely functional it couldn't change anything, and the point of programs is to change things. Programs have things like sockets,and database connections. We need to open them, use them, and close them. Lets say to do a Database query, you make a Connection, from the connection a Statement. You then use them and tear them down.
static class Statement {
void doIt(){ }
public void close() throws Exception{ }
}
static class Connection {
Statement createStatement(){
return new Statement();
}
public void close() {
System.out.println("shut it down");
}
}
In the old days Java you had your try/catch/finally. To be a "good citizen" you had to close things down in the reverse order. Like this:
@Test
void theOldDays(){
Connection connection = null;
Statement statement = null;
try {
connection = new Connection();
statement = connection.createStatement();
} finally {
if (statement != null){
try {
statement.close();
} catch (Exception ignored) {}
}
if (connection != null){
try {
connection.close();
} catch (Exception ignored) {}
}
}
}
That is a lot of typing. On one hand, I can see why some people complain on Java, but on the other there languages of choice may not even have a try/catch. This was smoothed over quite a while ago with AutoClosable and try-with-resources.
static class Statement implements AutoCloseable{
void doIt(){ }
public void close() throws Exception{ }
}
static class Connection implements AutoCloseable {
...
}
@Test
void nowADays(){
try (Connection conn = new Connection();
Statement statement = conn.createStatement()) {
statement.doIt();
} catch (Exception ignored) { }
}
It is hard to argue with that, it does exactly what you want, construct in order, tear down in reverse order.
Static imports
I used to dislike static imports, but eventually they won me over. When you ask, right after Junit changes Assert to Assertions and I had to do 5000000 search replace). static imports it is :)
@Test
void oldWay(){
Assertions.assertEquals(5, 5);
}
Now like this:
@Test
void newWay(){
assertEquals(5, 5);
}
You might say that is notthat special. But I have some cool little tricks I do with static imports. Everyone loves DSLs and matchers these days. Here is my poor mans DSL:
static class DslHelper{
static int divide(int x, int y){
return x/y;
}
static int multiply(int x, int y){
return x * y;
}
}
You can use it like this:
@Test
void soFunctional(){
assertEquals(10, divide(multiply(4, 5), 2));
}
Annotations
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Idea {
String type();
}
@Idea(type = "good")
public void useJava() throws NoSuchMethodException {
...
@Test
@Idea(type = "good")
public void useJava() throws NoSuchMethodException {
Idea isIdea = this.getClass()
.getMethod("useJava", null).getAnnotation(Idea.class);
assertTrue(isIdea.type().equals("good"));
}
Conclusion
The things in this blog aren't even the NEW features to Java. Many of these were probably added 10 years ago. Next time you see a post like, " ::scoff:: Java var! too little too late.", I'm not even touching on like all the functional goodies, or streams, or NIO, etc etc. Java Rocks!
Comments
Post a Comment