Languages - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Languages arrow Migrating Java Source Code To C#
Migrating Java Source Code To C#
By Jeff Friesen

Rate This Article: Add This Article To:

Your next C# project might require some feature that has already been expressed in Java source code. Instead of re-developing the feature, you can save time by migrating its Java source code to C#. In this article, Jeff Friesen shows you how.

Your next C# project might require some feature that has already been expressed in Java source code. Instead of re-developing the feature, you can save time by migrating its Java source code to C# (assuming no legal issues get in the way). You will probably want to use Microsoft's Java Language Conversion Assistant (JLCA) to automate this task.

Although your development environment most likely supports JLCA, this tool is always out of date -- JLCA cannot migrate Java language features newer than its release. For example, JLCA 3.0 (the most recent version) targets Java 1.3. Because Java has evolved significantly since its 1.3 release, JLCA 3.0 has limited value; you need to think about manual migration.

ADVERTISEMENT

Manually migrating Java source code to C# requires an understanding of their differences. This article reveals some of these differences via two examples that each migrate a Java application to its C# equivalent. The first example migrates a console-based word-counting application; the second example migrates a GUI-based temperature-conversion application.

Migrating console application source code

The console-based word-counting application takes a single command-line argument: the name of a text file whose words are to be counted. The file is opened, its contents are read (on a character-by-character basis), words are detected (a word is defined by a letter followed by zero or more letters/digits) and counted, and the total word count is output. Listing 1 presents the Java version of this application.

Listing 1: WordCount.java

// WordCount.java

import java.io.*;

class WordCount
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
          System.err.println("usage: java WordCount filename");
          return;
      }

      FileReader fr = null;
      try
      {
          fr = new FileReader(args[0]);

          int nWords = 0;
          while (nextWord(fr))
             nWords++;

          System.out.println("Number of words = " + nWords);
      }
      catch (IOException e)
      {
          System.err.println(e.getMessage());
      }
      finally
      {
          if (fr != null)
              try
              {
                  fr.close();
              }
              catch(IOException e)
              {
              }
      }
   }

   public static boolean nextWord(FileReader fr) throws IOException
   {
      int c;

      // Skip characters until a letter is read.

      while ((c = fr.read()) != -1)
         if (Character.isLetter((char)c))
             break;
      if (c == -1)
          return false;

      // Keep reading until a non-letter/non-digit is read.

      while (((c = fr.read()) != -1) &&
         (Character.isLetterOrDigit((char)c)));

      return true;
   }
}

If you have never been exposed to Java, you should still be able to understand Listing 1 because there are many similarities between Java and C# (thanks to their shared C/C++ heritage). Similarities include common keywords (such as class) and common statements (such as if and while). The equivalent C# application is presented in Listing 2.

Listing 2: WordCount.cs

// WordCount.cs

using System;
using System.IO;

class WordCount
{
   public static void Main(string[] args)
   {
      if (args.Length != 1)
      {
          Console.Error.WriteLine("usage: WordCount filename");
          return;
      }

      StreamReader sr = null;
      try
      {
          sr = new StreamReader(args[0]);

          int nWords = 0;
          while (nextWord(sr))
             nWords++;

          Console.WriteLine("Number of words = " + nWords);
      }
      catch (IOException e)
      {
          Console.Error.WriteLine(e.Message);
      }
      finally
      {
          if (sr != null)
              sr.Close();
      }
   }

   public static bool nextWord(StreamReader sr)
   {
      int c;

      // Skip characters until a letter is read.

      while ((c = sr.Read()) != -1)
         if (char.IsLetter((char)c))
             break;
      if (c == -1)
          return false;

      // Keep reading until a non-letter/non-digit is read.

      while (((c = sr.Read()) != -1) &&
         (char.IsLetterOrDigit((char)c)));

      return true;
   }
}

The first difference between Listings 1 and 2 is that of package versus namespace. Although packages and namespaces are equally a means for organizing types to prevent name conflicts, Java packages reflect a physical organization of types (this organization is typically backed by a filesystem's directory hierarchy or a hierarchy within a compressed file), whereas C#'s namespaces reflect a logical organization (namespaces do not map to directory paths).

Listing 1's import java.io.*; statement identifies the java.io package to the Java compiler. Similarly, Listing 2's using System.IO; directive identifies the System.IO namespace to the C# compiler. This information helps the compiler verify that an unqualified type (encountered in source code) belongs to the I/O package or namespace.

Note: By convention, Java's package names are specified entirely in lowercase (e.g. java.io). The C# convention uppercases the first letter of each namespace word, as in System.IO, for example.

While we are on the subject of package versus namespace, java.lang serves as Java's root package. In contrast, System is C#'s root namespace. Fundamental classes, including Object, are contained in the root package/namespace. Although Java doesn't require import java.lang.*; to import root package classes, C# requires using System; to import root namespace classes.

The entry point to a Java or C# console (or GUI) application is a method named main (Java) or Main (C#). This method's signature is public static void main(String [] args) in the Java world. For C#, this signature is almost identical. However, the C# equivalent's return type can be int (if the method returns an integer). Also, string[] args is optional.

Note: By convention, Java's method names are specified with the first word entirely in lowercase; subsequent words are capitalized. For C#, each word in a method name begins with an uppercase letter.

Listings 1 and 2 reveal that case differences also apply to property names. For example, Java expresses the array-length property as length, and C# expresses this property as Length.

Both listings reveal the Java and C# mechanisms for outputting data to the standard output/error streams. Java supports this capability through its java.lang.System root package class. On the C# side, the System.Console root namespace class supports this capability. Java's System.out.println() method is equivalent to C#'s Console.WriteLine() method.

For reading a text file's contents, Listing 1 uses Java's FileReader class and Listing 2 uses C#'s StreamReader class. In both listings, the actual character-reading and word-extraction logic takes place from within a nextWord() method. This method is called from within a try block because the character-reading operation is capable of throwing an IOException, which I've chosen to catch.

Note: For representing the Boolean type in source code, Java provides the boolean keyword, which is the equivalent of C#'s bool keyword. Like C#, Java expresses Boolean truth and falsehood with the true and false literals.

C#'s exceptions do not need to be caught or declared thrown. Java refers to such exceptions as unchecked. Java also supports checked exceptions, which must be handled or declared thrown by listing them in method signature throws clauses. Because FileReader()'s read() method throws IOException, which is checked, this unhandled exception is listed in nextWord()'s throws clause.

There are two more differences: FileReader's close() method is capable of throwing an IOException, which results in messy code that wraps this method call in a try block and includes a catch handler. Also, Java differentiates primitive types from equivalent classes. For this reason, Java will not let you invoke the Character class's methods via the char keyword.

Migrating GUI application source code

The GUI-based temperature-conversion application presents a text field for entering the number of degrees to convert. The application also presents a pair of buttons for converting this value to degrees Celsius or degrees Fahrenheit, and a label for presenting the conversion result. Listing 3 presents this application's Java source code.

Listing 3: Tempverter.java

// Tempverter.java

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;

public class Tempverter
{
   public static void main(String[] args)
   {
      JFrame frame = new JFrame("Tempverter");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      frame.getContentPane().add(createGUI());

      frame.pack();
      frame.setResizable(false);
      frame.setVisible(true);
   }

   static JPanel createGUI()
   {
      JPanel pnlMain = new JPanel();
      pnlMain.setLayout(new GridLayout(3, 1));

      JPanel pnlTemp = new JPanel();

      JLabel lblDegrees = new JLabel("Enter degrees");
      pnlTemp.add(lblDegrees);

      final JTextField txtDegrees = new JTextField(10);
      pnlTemp.add(txtDegrees);

      pnlMain.add(pnlTemp);

      pnlTemp = new JPanel();

      final JLabel lblResult = new JLabel(" ");
      pnlTemp.add(lblResult);

      pnlMain.add(pnlTemp);

      pnlTemp = new JPanel();

      ActionListener al;
      al = new ActionListener()
           {
               public void actionPerformed(ActionEvent ae)
               {
                  try
                  {
                      double value = Double.parseDouble(
                          txtDegrees.getText());
                      double result = (value-32.0)*5.0/9.0;
                      lblResult.setText("Celsius = "+result);
                  }
                  catch (NumberFormatException nfe)
                  {
                      JOptionPane.showMessageDialog(
                          null, "Bad input");
                  }
               }
           };

      final JButton btnToC = new JButton("To Celsius");
      btnToC.addActionListener(al);
      pnlTemp.add(btnToC);

      al = new ActionListener()
           {
               public void actionPerformed(ActionEvent ae)
               {
                  try
                  {
                      double value = Double.parseDouble(
                          txtDegrees.getText());
                      double result = value*9.0/5.0+32.0;
                      lblResult.setText("Fahrenheit = "+result);
                  }
                  catch (NumberFormatException nfe)
                  {
                      JOptionPane.showMessageDialog(
                          null, "Bad input");
                  }
               }
           };

      final JButton btnToF = new JButton("To Fahrenheit");
      btnToF.addActionListener(al);
      pnlTemp.add(btnToF);

      pnlMain.add(pnlTemp);

      return pnlMain;
   }
}

If you are unfamiliar with Java, Listing 3 will look somewhat alien. Essentially, the source code makes use of Java's Swing GUI toolkit, which is based on Java's Abstract Windowing Toolkit (AWT), to generate the GUI. Before I point out differences between Swing/AWT and C#'s Windows Forms GUI toolkit, take a look at Listing 4's equivalent C# source code.

Listing 4: Tempverter.cs

// Tempverter.cs

using System;
using System.Drawing;
using System.Windows.Forms;

public class Tempverter : Form
{
   private const int WIDTH = 200;
   private const int HEIGHT = 130;

   private const int ANCHORX = 15;
   private const int ANCHORY = 15;

   private const int SPACER1 = 5;
   private const int SPACER2 = 10;

   private Label lblResult;
   private TextBox txtDegrees;

   public Tempverter()
   {
      this.Text = "Tempverter";
      this.ClientSize = new Size(WIDTH, HEIGHT);

      // Prevent resizing and maximizing.

      this.FormBorderStyle = FormBorderStyle.FixedDialog;
      this.MaximizeBox = false;

      // Create and install the GUI.

      this.Controls.Add(CreateGUI());
   }

   Panel CreateGUI()
   {
      Panel pnl = new Panel();
      pnl.Size = new Size(WIDTH, HEIGHT);

      Label lblDegrees = new Label();
      lblDegrees.Text = "Enter degrees";
      lblDegrees.Location = new Point(ANCHORX, ANCHORY);
      lblDegrees.Size = new Size(lblDegrees.PreferredWidth,
                                 lblDegrees.PreferredHeight);
      pnl.Controls.Add(lblDegrees);

      txtDegrees = new TextBox();
      txtDegrees.Location = new Point(
          ANCHORX+lblDegrees.Width+SPACER1,
          lblDegrees.Location.Y-
          lblDegrees.Height/5);
      txtDegrees.Size = new Size (90,20);
      pnl.Controls.Add(txtDegrees);

      lblResult = new Label();
      lblResult.Location = new Point(
          ANCHORX,
          lblDegrees.Location.Y+lblDegrees.Height*2);
      lblResult.Size = new Size(
          lblDegrees.Width+SPACER1+txtDegrees.Width, 
          lblDegrees.Height);
      lblResult.BackColor = Color.AntiqueWhite;
      pnl.Controls.Add(lblResult);
        
      Button btnToC = new Button();
      btnToC.Text = "To Celsius";
      btnToC.Location = new Point(
          ANCHORX,
          lblResult.Location.Y+lblResult.Height*2);
      btnToC.Size = btnToC.PreferredSize;
      btnToC.Click += new EventHandler(btnToC_Click);
      pnl.Controls.Add(btnToC);

      Button btnToF = new Button();
      btnToF.Text = "To Fahrenheit";
      btnToF.Location = new Point(
          ANCHORX+btnToC.Width+SPACER2,
          btnToC.Location.Y);
      btnToF.Size = btnToF.PreferredSize;
      btnToF.Click += new EventHandler(btnToF_Click);
      pnl.Controls.Add(btnToF);

      return pnl;
   }

   public void btnToC_Click(object ob, EventArgs e)
   {
      double degrees = 0.0;

      try
      {
          degrees = Double.Parse(txtDegrees.Text);
      }
      catch (Exception) // Unlike Java, an exception 
                        // variable is not needed if
                        // it will not be used by the 
                        // exception handler.
      {
          MessageBox.Show("Bad input", 
              "Tempverter", MessageBoxButtons.OK, 
              MessageBoxIcon.Exclamation);
          return;
      }

      lblResult.Text = "Celsius = "+((degrees-32.0)*5.0/9.0);
   }

   public void btnToF_Click(object ob, EventArgs e)
   {
      double degrees = 0.0;

      try
      {
          degrees = Double.Parse(txtDegrees.Text);
      }
      catch (Exception)
      {
          MessageBox.Show("Bad input", "Tempverter",
              MessageBoxButtons.OK, 
              MessageBoxIcon.Exclamation);
          return;
      }

      lblResult.Text = "Fahrenheit = "+(
          degrees*9.0/5.0+32.0);
   }

   public static void Main()
   {
      Application.Run(new Tempverter());
   }
}

Listings 3 and 4 present several differences related to the GUI toolkit. The first difference has to do with GUI startup. Listing 3's main() method creates a closeable frame in which to house the GUI. This method next creates and installs a GUI into the frame, packs the GUI so that the frame presents no wasted space, makes sure the user cannot resize the frame, and displays the frame and its GUI to the user.

Invoking frame.setVisible(true); causes the Swing/AWT toolkit to start threads that run the GUI. In contrast, Listing 4's Main() method invokes Application.Run(new Tempverter()); to start the application's message loop (on the current thread), execute Tempverter's constructor to initialize this form and create/install its GUI, and display the form and its GUI to the user.

Note: C# provides the MessageBox class and assorted Show() methods for creating and displaying pre-defined dialog boxes. The Java equivalent is the JOptionPane class and assorted showMessageDialog() methods.

Another difference is with property access. In Listing 3, the frame's resizable property is cleared to false (so that the frame cannot be resized) by invoking the method setResizable. In contrast, Listing 4 prevents the Tempverter form from being resized by assigning a specific value to the form's FormBorderStyle property; no method call is made.

The property access difference is clearly seen when initializing controls. For example, Listing 3 initializes a button's text in a constructor call, final JButton btnToC = new JButton("To Celsius");, for example. In contrast, Listing 4 explicitly assigns this text to a property with btnToC.Text = "To Celsius"; as an example.

Note: Java's anonymous inner class feature is very convenient for GUIs. An anonymous inner class is an unnamed subclass of a nested type. It is often used to specify a component's event listener. Unfortunately, C# 2.0 doesn't support this feature.

In Listing 3, createGUI() uses a pair of anonymous inner classes as listeners for button action events. With assistance from Java's final keyword, these classes can access the local component variables lblResult and txtDegrees.

Keeping component variables local to createGUI() provides a clean separation between the GUI-creation code and the rest of the application. Because C# doesn't support anonymous inner classes, this separation is not as clean (see Listing 4).

Finally, Swing/AWT uses layout manager objects to position and size a GUI's components. A layout manager is first created and assigned to a container (as in pnlMain.setLayout(new GridLayout(3, 1)); which divides the container into three equal-size rows of one column each). Components that are then added to the container are typically laid out in a left-to-right and top-to-bottom order.

Although you can disable a Java GUI's layout managers and use absolute positioning and sizing, this is not often done because layout managers are better at positioning and sizing Java GUIs on different platforms. Because this is not a significant issue with C#/Windows platforms, absolute positioning and sizing are preferred , such as lblDegrees.Location = new Point(ANCHORX, ANCHORY); for example.

Summing Up

Before manually migrating Java source code to C#, you must understand the differences between these languages. The Java and C# source codes to the console-based word-counting and GUI-based temperature-conversion application examples revealed a variety of differences, including packages versus namespace and layout managers versus absolute positioning/sizing.

Lack of space prevented me from presenting a comprehensive treatment of C#/Java differences. For more insight into their differences, which will further help you should you need to migrate Java source code to C#, read Dare Obasanjo's article A Comparison of Microsoft's C# Programming Language to Sun Microsystems' Java Programming Language.




Discuss Migrating Java Source Code To C#
 
>>> Be the FIRST to comment on this article!
 

 
 
>>> More Languages Articles          >>> More By Jeff Friesen
 



HD VOIP Has Arrived (with Tony Konstner)

Play Video >

All Videos >

Google and blonde jokes?

Read now >

Favorite books!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.