Java a Bit on the Wordy Side
I was helping a friend of mine with a Java problem yesterday and couldn't help but notice this totally normal (for Java) file in his project:
import java.io.Serializable;
public class Contact implements Serializable
{
private String firstName;
private String lastName;
private String email;
private String phone;
public Contact()
{
this( "", "", "", "" ); // call four-argument constructor
} // end no-argument Contact constructor
// initialize a record
public Contact( String first, String last, String eml, String phn )
{
setFirstName( first );
setLastName( last );
setEmail( eml );
setPhone( phn );
} // end four-argument Contact constructor
// set first name
public void setFirstName( String first )
{
firstName = first;
} // end method setFirstName
// get first name
public String getFirstName()
{
return firstName;
} // end method getFirstName
// set last name
public void setLastName( String last )
{
lastName = last;
} // end method setLastName
// get last name
public String getLastName()
{
return lastName;
} // end method getLastName
// set email address
public void setEmail( String eml )
{
email = eml;
} // end method setEmail
// get email address
public String getEmail()
{
return email;
} // end method getEmail
// set phone number
public void setPhone( String phn )
{
phone = phn;
} // end method setPhone
// get phone
public String getPhone()
{
return phone;
} // end method getPhone
} // end class Contacts
Of course, I just had to rewrite that in Ruby to compare:
Contact = Struct.new(:first_name, :last_name, :email, :phone)
I'm not joking there, they are truly equivalent. The Ruby version has the private instance data, setters, and getters:
>> Contact = Struct.new(:first_name, :last_name, :email, :phone)
=> Contact
>> james = Contact.new("James", "Gray",
"james@grayproductions.net", "(405) 285-0536")
=> #<struct Contact first_name="James", last_name="Gray",
email="james@grayproductions.net", phone="(405) 285-0536">
>> james.last_name
=> "Gray"
>> james.phone
=> "(405) 285-0536"
>> james.phone = "405-285-0536"
=> "405-285-0536"
>> james.phone
=> "405-285-0536"
In fact, you could argue that the Ruby version is superior. Watch this:
>> james.each_pair do |var, val|
?> puts "#{var.to_s.capitalize}: #{val}"
>> end
First_name: James
Last_name: Gray
Email: james@grayproductions.net
Phone: 405-285-0536
=> #<struct Contact first_name="James", last_name="Gray",
email="james@grayproductions.net", phone="405-285-0536">
I can even reopen the class, if I need to add new methods:
>> class Contact
>> def full_name
>> "#{first_name} #{last_name}"
>> end
>> end
=> nil
>> james.full_name
=> "James Gray"
I now know why Java guys fear losing all their IDE goodies. I wouldn't want to type all of that code either!
This is one example that really makes me happy to not have a job in the Java world.
Struct is pretty darn handy, and being able to open the class back up and add more methods, properties, etc. is a real turn-on (can't do that in Python :-).
:-)
yeah, but your ruby version does not have any comments. i think in 2-3 years time, if you returned to ruby code listing, you would have no idea what that was about and why
Can you also overwrite the setters to do meaningful things like validation and other checks?
Could we have
check that the name is not empty?
Sure, setters are just methods:
Thanks, so you can change the way class members are accessed without the outside world having to change.
wx: I can't think of a very meaningful comment to adorn the utterly straight-forward one liner. ;)
Honestly, the code is about equally readable. One is just more verbose.
That said, you can point to a shortcoming in Java because of a design bug that appears to be there.
The Contact() constructor appears to exist in the form it does to ensure that the fields in Contact are never null (I say let them be null, but sometimes people want to go another way). The problem is that neither the setters nor the Contact( String first, String last, String eml, String phn) constructor enforce this logic. So you can very much end up with null fields.
Either the Contact() constructor should be changed to construct the object with nulls, or the setters and the other constructor need to be changed.
How a reasonable Java programmer would write the class. Still verbose, yet with retarded comments removed and using the Sun preferred whitespace standard, quite clear.
The real crime is the JavaBean standard which requires all those getters and setters. The above class in functionally equivalent to
and unless you have a need for JavaBeanieness, that's how you should code it. Don't get me wrong -- I love the Ruby, but the code posted was a bit of a straw man.
Jim's admittedly much cleaner rewrite has a serious drawback: the members are now accessed as instance variables rather than via accessor methods. The syntax and byte code for the two are different. That means that you could never compatibly introduce a method by that name to do something more complex than simply set the variable. In general I think folks will want to write accessors to reserve the right to introduce other behavior.
I'm sure you could write a class in java to behave the same way as a ruby Struct. However, even if you did manage to get the java down to one line through subclassing, java is still full of punctuation that ruby is able to drop (which makes it more readable to me). The other strength of ruby in my mind is the ease and integrated use of metaprogramming.
AmigoNico, you are wrong.
James code is better than Java's. Albeit it looks like he is accessing attributes, he is not. He is actually accessing getter/setter methods. However, unlike other languages (Python, Java, C++, etc) that force you to use a different syntax for getter/setters, ruby does not.
In ruby there is NO distinction between getter/setter functions and attributes from a syntax pov (It is the ONLY language I know that is smart enough to do that). It is one of the reasons I fell in love with the language.
As a matter of fact, ruby does not allow attributes to be public (unlike Python), keeping the class encapsulation.
A simple example: