Language Comparisons

Comparisons of programming languages, usually Ruby and something else.

10

MAR
2006

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!

Comments (12)
  1. Tim Morgan
    Tim Morgan March 17th, 2006 Reply Link

    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 :-).

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
    2. Gabor Farkas
      Gabor Farkas April 7th, 2006 Reply Link

      You can't do that in python

      class Parrot:
          pass
      
      
      p = Parrot()
      p.spam()
      >>> AttributeError: Parrot instance has no attribute 'spam'
      
      
      def spam(self):
          return "Spam comes from %s" % self
      
      Parrot.spam = spam
      
      p.spam()
      >>> Spam comes from <__main__.Parrot instance at 0xb7b2766c>
      

      :-)

      1. Reply (using GitHub Flavored Markdown)

        Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

        Ajax loader
  2. wx
    wx April 16th, 2006 Reply Link

    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

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
    2. James Edward Gray II
      James Edward Gray II April 17th, 2006 Reply Link

      wx: I can't think of a very meaningful comment to adorn the utterly straight-forward one liner. ;)

      1. Reply (using GitHub Flavored Markdown)

        Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

        Ajax loader
  3. Felix
    Felix April 16th, 2006 Reply Link

    Can you also overwrite the setters to do meaningful things like validation and other checks?

    Could we have

    james.first_name = ""
    

    check that the name is not empty?

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
    2. Jules
      Jules April 16th, 2006 Reply Link

      Sure, setters are just methods:

      def first_name=(value)
        if value.length != "" 
           self[:first_name] = value 
        end 
      end
      
      1. Reply (using GitHub Flavored Markdown)

        Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

        Ajax loader
      2. Felix
        Felix April 16th, 2006 Reply Link

        Thanks, so you can change the way class members are accessed without the outside world having to change.

        1. Reply (using GitHub Flavored Markdown)

          Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

          Ajax loader
  4. Christopher Smith
    Christopher Smith April 18th, 2006 Reply Link

    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.

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
  5. Jim
    Jim May 5th, 2006 Reply Link

    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.

    import java.io.Serializable;
    
    public class Contact implements Serializable {
       private String firstName;
       private String lastName;
       private String email;
       private String phone;
    
       public Contact(){ } 
    
       public Contact( String first, String last, String eml, String phn ) {
           firstName = first;
           lastName = last;
           email = eml;
           phone = phn;
       } 
    
       public void setFirstName( String first ) {
          firstName = first;
       }
    
       public String getFirstName() {
          return firstName;
       }
    
       public void setLastName( String last ) {
          lastName = last;
       }
    
       public String getLastName() {
          return lastName;
       }
    
       public void setEmail( String eml ) {
          email = eml;
       }
    
       public String getEmail(){
          return email;
       } 
    
       public void setPhone( String phn ){
          phone = phn;
       }
    
       public String getPhone(){
          return phone;
       }
    }
    

    The real crime is the JavaBean standard which requires all those getters and setters. The above class in functionally equivalent to

    public class Contact implements Serializable {
       public String firstName;
       public String lastName;
       public String email;
       public String phone;
    
       public Contact(){ } 
    
       public Contact( String first, String last, String eml, String phn ) {
           firdtName = first;
           lastName = last;
           email = eml;
           phone = phn;
       }
    }
    

    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.

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
    2. AmigoNico@gmail.com
      AmigoNico@gmail.com May 13th, 2006 Reply Link

      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.

      1. Reply (using GitHub Flavored Markdown)

        Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

        Ajax loader
      2. gga
        gga December 24th, 2006 Reply Link

        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:

        class A
          def initialize
              @x = 0
          end
        end
        
        a = A.new
        puts a.x
        # NoMethodError: undefined method `x'
        #         for #<A:0x2b484690e7f0 @x=10> from (irb):10
        
        ## Attribute x is private.  
        ## Create a r/w accessor for it.
        
        class A
          attr_accessor :x
        end
        
        puts a.x
        0
        
        #
        # Now, change the reader to calculate
        # something
        #
        class A
          def x
             @x - 20
          end
        end
        
        puts a.x
        -20
        a.x = 5
        puts a.x
        -15
        
        1. Reply (using GitHub Flavored Markdown)

          Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

          Ajax loader
  6. djfoobarmatt
    djfoobarmatt September 15th, 2006 Reply Link

    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.

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
Leave a Comment (using GitHub Flavored Markdown)

Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

Ajax loader