As a Java programmer, I'm not completely convinced that brevity is always good. I know that I can write some pretty unreadable brief code. ~(~0>>>prefixLength) is a nice little example. It converts a prefix length, e.g., /24, in a network number, into a netmask, e.g., 255.255.255.0 (as an unsigned int). However, for this article, I'll put readability on the backburner, somewhat. When I stumbled across Sometimes Less is More by Peter Szinek, who appears to like being photographed with camels, I found that some of the Java code posted seemed to be written by, well, someone who didn't like Java. In this post I'll try to suggest better Java examples. I will omit imports and method declarations unless they are relevant. 1. Ruby:
10.times { print "ho" }
or
print "ho" * 10
Perl possibly has a more sane syntax, print "ho" x 10; - this way '*' doesn't mean both multiplication and repetition. I'm not too bothered either way on this.
The article actually gave no Java equivalent, here's one:
out.println(format("%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s","ho"));
out
is System.out
, and format
is String.format
, imported statically. Obviously if you make a method to do this, the calling code becomes very very short: out.println(repeat("ho",10));
repeat
comes from cirrus.hibernate.helpers.StringHelper
(found via Google Code Search).
2. Ruby: if 11.odd? print "Odd!"
The article showed this as the Java equivalent:
if ( 1 % 2 == 1 ) System.err.println("Odd!");
However, this only works for positives and zero. I'd prefer:
if (1%2!=0) out.println("Odd!");
(-3%2==-1)
Again, with prewritten methods, this can become clearer - there is a prewritten 'odd' method in the JDK 1.4 demos, should this prove hard to write yourself.
if (odd(11)) print("Odd!"); //let's assume print is a method that does System.out.println.
3. Ruby: 102.megabytes + 24.kbytes + 10.bytes
Java: 102 * 1024 * 1024 + 24 * 1024 + 10
I'd prefer this a little: 102<<20 + 24<<10 + 10;
or with a little predefinition: 102*MB+24*KB+10
. I really wonder what the bytes method does in Ruby!
4. Ruby: print "Currently in the #{2.ordinalize} trimester"
Java: System.err.println("Currently in the" + Util.ordinalize(2) + "trimester");
My suggested Java: out.printf("Currently in the %s trimester",ordinalize(2));
5. Ruby: puts "Running time: #{1.hour + 15.minutes + 10.seconds} seconds"
Java: System.out.println("Running time: " + (3600 + 15 * 60 + 10) + "seconds");
Suggested Java: out.printf("Running time: %d seconds", 1*HOURS+15*MINUTES+10);
6. Ruby: 20.minutes.ago
Java: new Date(new Date().getTime() - 20 * 60 * 1000)
Suggested Java: new DateTime().minusMinutes(20)
- DateTime is from Joda Time.
7. Ruby: 20.minutes.until("2006-10-9 11:00:00".to_time)
Java: Date d1 = new GregorianCalendar(2006,9,6,11,00).getTime();
Date d2 = new Date(d1.getTime() - (20 * 60 * 1000));
Suggested Java: new DateTime(2006,10,9,11,0,0).minusMinutes(20)
8. Ruby:
class Circle
attr_accessor :center, :radius
end
Java: Too long, see the original article!
Suggested Java:
class Circle
{
public Coordinate center;
public float radius;
}
"a simple class definition having 10 fields in Java will have 80+ lines of code compared to 1 lines of the same code in Ruby."
For a lot of classes, many of those fields will be immutable anyway, or at least not exposed via getters/setters. In the case of an anonymous class, there are zero extra lines of code for them.
9. Ruby: stuff = []
stuff << "Java", "Ruby", "Python" #add some elements
Suggested Java: List<String> stuff=arrayList();
stuff.addAll(asList("Java","Ruby","Python"));
10. Ruby: stuff = [”Java”, “Ruby”, “Python”]
Suggested Java: List<String> stuff=asList("Java","Ruby","Python");
11. The author complains that you have to sort arrays using Arrays.sort(array) instead of array.sort() - however, this can become sort(array) via a static import.
12. I think the stuff about stacks and arrays would be solved by using java.util.Stack, which has pop/push/subList etc. I don't see a great need for the static implementation to appear as part of the object though. I prefer thin objects.
13. The author seems to think that adding 'nil' values to an array/list when you try to add to an index beyond the end of the array/list is a good thing. I rather like the little protection that Java gives in that if you want null values you have to add them yourself (except with the new String[10]
syntax).
14. Ruby: File.read('test.txt').scan(/.*?\. /).each { |s| puts s if s =~ /Ruby/ }
Suggested Java:
import static java.lang.System.out;
import java.io.*;
class Test {
public static void main(String[] args) throws IOException
{
File file=new File("filename");
byte[] bytes=new byte[(int)file.length()];
DataInputStream input=new DataInputStream(new FileInputStream(file));
input.readFully(bytes);
String[] sentences=new String(bytes,"ASCII").split("\n");
for (String sentence: sentences)
if (sentence.indexOf("Ruby")!=-1)
out.println(sentence);
input.close();
}
}
I expect that I could mimic the Ruby way, given time and inclination. The end result would be this, with supporting methods:
File.read("test.txt").scan("\n").each(ifMatches("Ruby",print));
15. Ruby:
tree = a {
b { d e }
c { f g h }
}
Suggested Java:
Tree tree=tree("a",
tree("b",leaves("d","e")),
tree("c",leaves("f","g","h"))
);
And just as a general comment, I'd gravitate closer to Haskell, with its type inference and very powerful static type system, than towards Ruby, Python et al, because I like the freedom to be able to mess around with my code, knowing that there is an automatic eye-over-my-shoulder that's going to tell me when I do something stupid.
This post is not to invalidate the original article; it's clear that the Ruby way of doing things is obviously much simpler in many cases. However, often the idiomatic Java way is not the best anyway. Personally I've been experimenting with functional programming from within Java (is this like asking your wife to dress up as someone else?), and I think closures could really help in any future 'brevity wars'.