Java, J#, and the .NET Framework
J# includes independently developed libraries that provide the same functionality as that of the JDK 1.1.4 libraries, which makes it easier to take existing Java code and port it to .NET. If your Java application uses the Abstract Window Toolkit (AWT), JDBC or other features of the JDK 1.1.4 class libraries, you can compile your source code using the J# compiler and generate a .NET executable. However, there are a few features that Microsoft does not support, and there are some areas of overlap between the JDK 1.1.4 class libraries and the .NET Framework Class Library that might cause confusion. We'll discus these in the
next
few sections.
Packaging and Locating Classes
Java uses
packages
as containers for classes. In many
implementations
, a package is a directory structure held on disk or in a compressed ZIP folder. For example, in the JDK 1.1.4 under Windows, the compressed folder classes.zip contains the JDK library. Classes.zip is located in the \JDK1.1.4\lib folder by default. If you're using Windows XP, you can examine the contents of classes.zip. You'll see that the folder contains compressed folders called java, sun, and sunw. (The latter two folders contain extension classes provided by Sun Microsystems and are not
strictly
part of the standard JDK library.) If you
open
the java folder, you'll see a range of further folders corresponding to the various packages in the JDK library, as shown in Figure 3-5.
Figure 3-5. The JDK 1.1.4 class library
If you open one of these folders ”lang, for example ”you'll find all the classes in the java.lang package as well as a further folder called reflection, which contains the classes in the java.lang.reflection package.
In version 1.2 of the JDK, packages can also be held in a Java Archive (JAR) file, which is similar to a ZIP file except it contains a manifest describing the contents of the archive, which makes it quicker to search.
At run time, the class loader in the JVM uses the
CLASSPATH
environment variable to provide a list of locations used for resolving class references. These locations can be folders, references to ZIP files, or both. If the
CLASSPATH
variable does not exist, the JVM uses a default
path
that includes the classes.zip file in the lib subfolder, under the folder created for the JDK when it was installed. Each location in the
CLASSPATH
is examined in
turn
until the required package and class is located or the
CLASSPATH
is exhausted. If the class is found, the JVM will load it and use it. If not, it will generate a
NoClassDefFoundError
.
As you might recall, the mechanism used by the common language run time for locating and loading classes is quite different. For one thing, the common language runtime uses assemblies and namespaces rather than ZIP files and packages. Like Java, J# also uses the
import
statement, but you perform namespace resolution at compile time by specifying the assemblies being used with the
/reference
flag to the vjc compiler. At run time, the J# class loader uses the common language runtime mechanisms described in Chapter 2 to locate and load the correct assembly and class. J# does not use the
CLASSPATH
variable. The
Class.forName
methods
, which you can use to load a class dynamically, also use the assembly search mechanisms of .NET rather than the
CLASSPATH
variable.
Incidentally, you'll find the Microsoft libraries that support the functionality of the JDK 1.1.4 libraries used by J# in the assembly Vjslib.dll located under \Windows\Microsoft Visual JSharp. NET\Framework\<version>. Figure 3-6 shows this assembly being examined using ILDASM.
Figure 3-6. The contents of the Vjslib.dll assembly
The Java Class Hierarchy
If you're familiar with the JDK, you know that it implements an object-oriented hierarchy of types. The same is true of the .NET Framework. There are similarities and differences between these two hierarchies, and one task that J# has to perform is provide access to both in as seamless a manner as possible. This task is complicated by
name
clashes
. For instance, at the top of the JDK hierarchy is the generic
Object
type in the java.lang package. Likewise, there is an
Object
class at the top of the .NET Framework hierarchy in the
System
namespace. Also, both the JDK and .NET contain a
String
type, arrays, exceptions, and their own primitive types. The J# compiler must handle all of these potential conflicts.
J# Objects
In the Java language, the unqualified type
Object
is resolved as
java.lang.Object
. In the common language runtime, the unqualified type
Object
is resolved as
System.Object
. J# addresses this ambiguity by effectively making
java.lang.Object
an alias for
System.Object
, so they are both the same thing. When a J# object is compiled, any
java.lang.Object
methods called using it (such as
toString
,
wait
,
getClass
, and so on) that are not part of the
System.Object
class are silently redirected to the
com.ms.vjsharp.lang.ObjectImpl
class implemented in the vjslib.dll assembly. We won't get into the details of how this is done because they might change in the future, but suffice it to say that in J#
java.lang.Object
and
System.Object
are interchangeable. (You can even execute
java.lang.Object
methods against a variable explicitly declared as
System.Object
, and vice-versa.)
All the classes you create in J# are descended directly from
System.Object
, and you can assign any J# class object (as opposed to a primitive J# type) to a
System.Object
with impunity. Effectively, the types in Microsoft's version of the JDK are treated just like the types in any other assembly, and you can even access them from other languages. You add a reference to the Vsjlib.dll assembly, and then you can use the types in the
java
namespace (and any other namespaces defined in Vjslib.dll). Here's a fragment of C# that creates a Java
Long
variable:
usingSystem;
usingjava;
classMyClass
{
publicstaticvoidMain(string[]args)
{
java.lang.Longtest=newjava.lang.Long(99);
}
}
Primitive Types
The Java language distinguishes between object types (classes) and primitive types. In the JDK, all object types are
instantiated
using the
new
operator, created on the heap, and thereafter subject to the memory management implemented by the JVM. Primitive types (the numeric types,
char
, and
boolean
) are created on the stack and come into existence automatically when execution
reaches
the point at which they are declared. Primitive types are removed as soon as they go out of scope and are not garbage collected in the same way that object types are.
The same is true in the common language runtime ”so far. However, in Java the primitive types are built into the JVM. In contrast, the .NET Framework Class Library defines the primitive types as structures in the
System
namespace. A structure is a bit like a class. It can have methods as well as data, but it is created on the stack. For example, the native integer type in the common language runtime is actually the structure
System.Int32
. When you create a variable of type
Integer
in Visual Basic .NET, or
int
in C#, these are actually aliases for
System.Int32
. In many ways, the primitive types of the common language runtime are more like the wrapper classes (such as
java.lang.Integer
) of the JDK. In languages other than J#, the common language runtime further blurs the distinction between the primitive and object types by allowing you to directly assign a primitive to an object. For example, in C# you can use statements such as this:
//C#
intanInt=99;
Objectthing=anInt;
If you think about this from a Java perspective, it looks quite odd because in Java the primitive types and the object types do not mix. To do the equivalent in Java or J#, you would have to write this:
//JavaandJ#
intanInt=99;
Objectthing=newInteger(anInt);
What the
preceding
code does is use the
Integer
wrapper class to create an object that contains a copy of the integer value inside it. You can then assign the
Integer
to an
Object
instance. In essence, what you're doing is creating a "box" on the heap,
sticking
an integer value in that box, and then referencing it with the
Object
, as shown in Figure 3-7.
Figure 3-7. Using a wrapper
The common language runtime actually
performs
the same trick, but it does so silently, without you having to write any code to do it. This is called
boxing
. The common language runtime also performs
unboxing
, which allows you to assign an object to a primitive type:
//C#
intanInt=99;
Objectthing=anInt;//boxing
anInt=(int)thing;//unboxing
Unboxing must use a cast, and if the object does not refer to a boxed value of the appropriate type, the common language runtime will raise an
InvalidCastException
.
The JVM does not perform boxing, so it should come as no surprise that it does not perform unboxing either. Instead, many of the wrapper classes that you use for boxing provide methods that allow you to gain access to the primitive data inside. For example, the
Integer
class has the
intValue
method:
//JavaandJ#
intanInt=99;
Objectthing=newInteger(anInt);
anInt=((Integer)thing).intValue();
What this all means is you cannot treat the Java primitive types in the same way as the .NET primitive types when you write J# code. Nevertheless, in the .NET world, the common language runtime performs the role of the JVM, and the J# compiler uses the same underlying Microsoft Intermediate Language (MSIL) data types used by the .NET primitive types. To see what we mean, look at this method implemented in J#:
publicstaticdoubleCalculatePay(intnumHours,doubleratePerHour)
{
returnnumHours*ratePerHour;
}
The MSIL code for the
CalculatePay
method looks like this:
.methodpublichidebysigstaticfloat64CalculatePay(int32numHours,
float64ratePerHour)cilmanaged
{
}//endofmethodJavaCalcPay::CalculatePay
Notice how the Java
double
type is compiled into an MSIL
float64
, and a Java
int
becomes an
int32
. Suppose you write the equivalent method in another language that uses the .NET types (Visual Basic .NET, for example):
PublicFunctionCalculatePay(numHoursasInteger,_
ratePerHourasDouble)asDouble
ReturnnumHours*ratePerHour
EndFunction
Here is the MSIL code for this version:
.methodpublicstaticfloat64CalculatePay(int32numHours,
float64ratePerHour)cilmanaged
{
}//endofmethodVBCalcPay::CalculatePay
You can see that the Visual Basic
Integer
(which is actually a
System.Int32
) is also compiled into an
int32
, and the Visual Basic
Double
(
System.Double
) becomes a
float64
. So although the .NET and Java primitive types are different when you're writing code (J# retains the semantics of Java primitive types), by the time your code has been compiled, they are interchangeable. You can call a method written in J# from Visual Basic and pass parameters whose underlying types are the same in both languages.
Value Types
The primitive types in .NET are examples of
value types
. In .NET, a value type is a type that is created on the stack rather than the heap. The most common value types used by .NET are structures and enumerations. The .NET Framework Class Library contains
numerous
value types, and if you're writing programs in C#, Visual Basic .NET, or C++, you can also define your own. Java and J# do not have value types, and you cannot create them using J#. However, you can
employ
existing value types from J#. With the exception of the primitive types, the J# compiler performs automatic boxing and unboxing conversions when you access them. You just use them as if they were ordinary Java types.
One common example of a value type in .NET is the enumeration. An enumeration is a set of named constants, usually applied to a specific domain. For example, the
DayOfWeek
enumeration in the
System
namespace defines constants representing each day of the week. You can create a variable whose type is
DayOfWeek
, assign it one of the defined constant values, and read it back. You can also compare a
DayOfWeek
variable against the constant
DayOfWeek
values. The following code creates a variable called
day
,
assigns
it the value
Monday
, and then prints it out. This is all valid J# code.
System.DayOfWeekday=newSystem.DayOfWeek();
day=System.DayOfWeek.Monday;
System.out.println("Thedayis " +day.ToString());
Internally, enumeration constants are actually integer values, and you can cast an enumeration constant to and from a Java
int
. You can even perform arithmetic on them, but be careful not to go out of range ”if you subtract 1 from a variable holding
DayOfWeek.Sunday
(which has the value 0), you'll end up with
-1
, which doesn't stand for any day of the week.
Note
Although you can create local
variables
using the value types defined in the .NET Framework Class Library, J# does not currently allow you to use value types such as
System.Int32
or
System.Int64
in any definitions that generate metadata (method signatures, fields, and properties). Instead, you should use the corresponding equivalent primitive types (
int
,
long
, and so on).
Strings
The .NET Framework defines a
String
type in the
System
namespace. There is also a
String
type in the java.lang package in the JDK. The situation is similar to that of the
Object
types, and the solution to any ambiguity is equally similar. When you use J#,
System.String
and
java.lang.String
are interchangeable. You can assign a
System.String
to a
java.lang.String
, and you can execute
System.String
and
java.lang.String
methods against objects of either type.
Arrays
Arrays in Java are first-class objects that descend from
java.lang.Object
. As such, each array has a type, fields, and methods. The class for a given Java array is actually generated by the Java compiler. Programmers tend not to concern
themselves
with such details, but if you're
curious
, you can see the name of the class by printing this array:
int[]data=newint[5];//Couldbeanysizeforthisdemo
System.out.println(data);
The results will vary. Using the J# compiler, the
data
array was called
[I@1f
. Using the JDK 1.1.4 compiler from Sun, the data array was called
[I@273d3c
. These
names
will vary.
Array Class Names in Java
The class name that Java generates for an array comprises a
[
for each dimension, followed by a letter or a string indicating the type of the array elements, followed by an
@
sign, and then a unique hexadecimal integer. The name
[I@5b
indicates that the array has one dimension and contains integers. An array of
floats
would start with the prefix
[F@
, and a two-dimensional array of
long
s would start with the prefix
[[J@
. Why
J
? Well,
L
is used if the array contains nonprimitive types, and
J
is the next available letter. For example, a one-dimensional array of
Object
s has the prefix
[Ljava.lang.Object;@
.
|
The .NET Framework also contains arrays. .NET has the
Array
class defined in the
System
namespace, and every non-J# .NET array inherits from
System.Array
. The .NET arrays have their own properties and methods, which are different from but sometimes similar to those of Java arrays. For example, Java arrays have a
length
field that you can examine to determine the length of the array, and .NET arrays have a
Length
property. (Note the uppercase L). What this means is that J# arrays and arrays used by the other .NET languages are not the same. Happily, this is only of minor concern. First, you cannot create
System.Array
objects directly in J#, so there is little scope for ambiguity. Second, the J# compiler will
freely
convert from Java arrays to .NET arrays and back again. You can pass arrays from your C# code as parameters to methods written in J#, and vice-versa.
Java allows you to create multidimensional arrays. In the following example, variable
i
is a two-dimensional array of
int
elements:
int[][]i;
Strictly speaking, variable
i
is an array of arrays of
int
elements. The Java language
permits
such arrays to be ragged ”the second dimension can vary in length. For example, in the
data
array shown below, the second dimension comprises five elements in the first row of the array and three elements in the second row:
int[][]someData={{1,2,3,4,5},{6,7,8}};
The .NET Framework supports
jagged
arrays (notice the different
nomenclature
”
jagged
rather than
ragged
), which are
essentially
the same, and the J# compiler will convert Java ragged arrays into .NET jagged arrays and back again. However ”and this is a big however ”jagged arrays are not part of the Common Language Specification (CLS), and some languages, including Visual Basic .NET, do not have them! Instead, the CLS
mandates
rectangular
arrays, where each dimension length is fixed and therefore cannot vary once it has been defined. In C#, a 5-by-3 two-dimensional rectangular array is declared using the following syntax:
int[,]moreData=newint[5,3];
Rectangular arrays are different beasts from jagged arrays, and you cannot assign a two-dimensional jagged array to a two-dimensional rectangular array (not even in C#). This posed a
slight
problem for J#. The Java language has only ragged arrays, which
meant
that J# would not be able to use any of the methods in the .NET Framework Class Library that passed or returned multidimensional arrays (because they're all rectangular, for CLS compliance). So Microsoft bit the bullet and added rectangular arrays to J#.
Exceptions
Both Java and .NET allow you to define, throw, and catch exceptions. Java exceptions descend from the generic
Exception
class in the java.lang package, but .NET exceptions descend from
System.Exception
. Java exceptions and .NET exceptions are different types that expose a similar functionality. To make interoperability with Java possible, an unqualified
Exception
refers to the
java.lang.Exception
class:
try
{
}
catch(Exceptione)//defaultstojava.lang.Exception
{
}
If you want to catch the .NET
Exception
class instead, you must qualify the
Exception
object:
try
{
}
catch(System.Exceptione)
{
}
One result of the way in which the
Exception
classes are defined with J# is that
System.Exception
will mask and catch
java.lang.Exception
(and consequently any other Java exceptions). However, the
converse
is not true ”
java.lang.Exception
will not catch
System.Exception
or any other .NET exceptions. So if your application throws a
System.Exception
or any of the other .NET exceptions, using a
catch(Exception e)
statement will not actually catch them. (You'll most likely get an error when you compile your code, informing you that possible exceptions have not been caught.) This has significant implications if you're using the .NET Framework Class Library with J# because many of the methods raise .NET exceptions. You should be prepared to handle them.
Interfaces
Java has interfaces, and .NET has interfaces. A J# class can implement a .NET interface, and a class written using one of the other .NET languages can implement a J# interface. An example C# interface is shown here:
//C#
publicinterfaceCBase
{
voiddoProcessing();
}
The J#
JChild
class that
follows
can implement this interface and even add a
throws
clause if the method can raise an exception:
//J#
publicclassJChildimplementsCBase
{
publicvoiddoProcessing()throwsException
{
thrownewException("Processingfailed");
}
}
The important point to remember when you use interfaces is that the specified methods must use types that are available in the implementing languages. (Trying to implement a C++ interface that uses pointer types in J# could be tricky, for example.) If you stick to the types in the CLS, you should not have any problems.
You might need to perform a bit of translation when you implement interfaces that define properties. For example, the following interface is valid in C#:
//C#
interfaceCSharpInterface
{
intdata
{
get;
set;
}
}
An implementation in J# would look like this:
publicclassJSharpPropertiesimplementsCSharpInterface
{
/**@property*/
intget_data()
{
}
/**@property*/
voidset_data(intvalue)
{
}
}
Other Issues
You should be aware of some other issues that will affect the way that your J# programs interact with the .NET Framework, as described in the following sections.
Methods with Variable
Numbers
of Arguments
The CLS allows you to create methods that take variable numbers of arguments (as opposed to creating overloaded methods). The following C# method allows the caller to pass in a variable number of
int
parameters. The
params
keyword indicates that the array parameter that follows can be interpreted as a list of
int
elements by the compiler. If you are a C or C++ developer, you might be familiar with
varargs
, which performs a similar function:
//C#
publicvoidUseParams(paramsint[]list)
{
for(inti=0;i<list.Length;i++)
{
//processelementi
}
}
If you want to pass a selection of different types, you can create a
params
array comprising
Object
elements. You can invoke this method from C# using statements such as this one:
//C#
UseParams(99,100,120);
UseParams(5);
At run time, the actual parameters will be packaged into an
int
array and passed to the
UseParams
method as the formal parameter list. However, if you try calling the method from J# using the same syntax, you'll receive a compile-time error because the Java language does not permit methods that take variable parameter lists. Instead, you must manually package the parameters into an array yourself, as shown here:
//J#
int[]i={99,100,120};
UseParams(i);
Console I/O
The JDK provides the
System.out PrintStream
object, which has the method
println
for displaying information on the console. The
println
method is overloaded, which allows you to output a single value of any type ”most often a
java.lang.String
. The .NET Framework contains the
Console
object in the
System
namespace, which fulfills a similar role. It has a method called
WriteLine
, which you can use to display information to the screen. You can print Java strings, objects, and primitive types, as shown here. (Objects will be rendered using the
ToString
method inherited from
System.Object
.)
importSystem.*;//ConsoleisintheSystemnamespace
Stringmessage= "Hello,World";
Console.WriteLine(message);
You might find it more
convenient
to use
Console.WriteLine
rather than
System.out.println
when you develop J# programs.
Getting keyboard input is notoriously
troublesome
in Java. The
System.in InputStream
object
supplies
a
read
method that you can use to read individual bytes from the keyboard, but it is all very low-level and
error-prone
. In contrast, the
System.Console
class in the .NET Framework Class Library contains the
ReadLine
method, which reads a
System.String
from the keyboard, up to the next
carriage
return. This method is easier to use, as this code shows:
importSystem.*;
System.Stringinput=Console.ReadLine();
The
ubyte
Type
Microsoft has tried hard to not make too many additions to the Java language in J#. You've already seen that rectangular arrays were added to maintain compliance with the CLS. One data type in the CLS that is used by a number of methods in the .NET Framework Class Library is
Byte
, which can hold 8-bit unsigned values. Java also has a
byte
data type, but it is signed. (.NET has the
SByte
data type for signed 8-bit values.) Microsoft therefore created the data type
ubyte
for J#, for 8-bit unsigned data. Try not to get too
confused
”a .NET
SByte
corresponds to a J#
byte
, and a .NET
Byte
corresponds to a J#
ubyte
!
Threads
The JDK defines the
Thread
class in the java.lang package. Classes that extend
Thread
and override the
run
method can be executed asynchronously. If an object instantiates a
Thread
object and calls the
start
method, the JVM will invoke the
Thread
object's
run
method and return to the calling object without waiting for
run
to finish. Both the calling object and the
Thread
object can then execute concurrently. The
Thread
class also provides the
stop
method, which can halt a
Thread
object in its tracks, and the
join
method, which causes the creating object to wait for the
Thread
object to complete. Classes can define methods as
synchronized
, which
prevents
two concurrent threads from executing the same code in the same object at the same time ”the second thread will have to wait for the first to exit the method.
The following classes
illustrate
a simple
Thread
class called
WorkerThread
. The
run
method prints the values 0 through 499. The
ThreadRunner
class creates a new
WorkerThread
and invokes its
run
method asynchronously by executing the
start
method. The
ThreadRunner
then prints a farewell message and finishes.
JDK Threads
classWorkerThreadextendsThread
{
publicvoidrun()
{
for(inti=0;i<500;i++)
System.out.print(i+ " ");
}
}
classThreadRunner
{
publicstaticvoidmain(String[]args)
{
WorkerThreadwt=newWorkerThread();
wt.start();
System.out.println("ThreadRunnerfinished");
}
}
You can compile and run this program using the JDK or J# ”it will work with either. On our
uniprocessor
machine, both the JDK and the J# implementations cause the "ThreadRunner finished" message to be displayed before the numbers 0 to 499 are printed. The results you get when you run the program might not be the same,
especially
if you have a multiprocessor machine.
Bear in mind that when you use JDK threads, the JVM implements the threading model itself. Many operating systems, including Windows, have native support for threads, and the JVM might use this facility behind the scenes, but it will use its own data structures internally for managing them. The reason for this, once again, is portability. Any Java application can use threads whether the underlying platform supports them or not, and all implementations of the JDK must provide a consistent implementation of the
Thread
class.
Windows has its own multithreading capability, and the common language runtime permits managed access to it through the
System.Threading
namespace. This namespace is feature-rich compared to the JDK, and it exposes the
System.Threading.Thread
class, which you can use from J#. To some extent, common language runtime threads and JDK threads will
interoperate
”they can be coordinated with each other using either
synchronized
methods or common language runtime
Monitor
objects. However, the
System.Threading.Thread
class is different from
java.lang.Thread
and they are not interchangeable. A common language runtime thread cannot be a member of a JDK
ThreadGroup
, for example. The main reason that the
java.lang.Thread
class exists is to support existing JDK code that you don't want to rewrite. If you're undertaking new development that involves threads, you should use the
System.Threading
namespace in preference to the
java.lang.Thread
.
You have probably gathered that threads are another area in which there's a namespace
clash
. By default, the J# compiler assumes that an unqualified
Thread
reference refers to a
java.lang.Thread
. If you want to use a common language runtime thread, you must qualify it as a
System.Threading.Thread
. Threads under the common language runtime will be explained in detail in Chapter 8.
Omissions from the JDK
Microsoft has implemented much of the JDK 1.1.4 functionality. The omissions include RMI, which we already mentioned, and there is also no support for the Java Native Interface (JNI) or for applets. However, .NET, together with Windows, does supply eminently suitable alternatives. Instead of RMI, you can use .NET Remoting or COM. JNI has been
replaced
by J/Direct (originally introduced with Visual J++) and the Platform Invoke service, and applets have been superseded by ASP.NET Web Forms.
Migrating to J#
Microsoft supplies some useful tools with the Visual Studio.NET IDE to help you migrate existing JDK and Visual J++ 6.0 applications to J#. You can migrate the source code, or you can convert class files. We'll look at these possibilities next.
Migrating Source Code
By now, you should appreciate that you can compile ordinary
.java
source code using the jc compiler to produce DLLs and EXEs. The compiler can take source code that conforms to the Java Language Specification version 2.0. If you have existing J++ 6.0 solutions, you can open them in Visual Studio .NET if you have Visual J# installed. Solution and project files will be upgraded automatically (the Visual J# Upgrade Wizard takes you through the process), but you should be aware of a few issues:
-
The
CLASSPATH
variable is no longer used. Instead, you must add references to each assembly that implements the packages your application uses. This means you must first have upgraded those packages and converted them into assemblies.
-
References to COM
components
are not preserved automatically. You must add them manually to the Visual J# project either during the upgrade process (the wizard will prompt you) or using the Add Reference command on the Project menu.
-
The project upgrade process ignores Microsoft Visual SourceSafe (VSS) settings. You must check out all source files from VSS before upgrading.
-
Not all deployment options are preserved, and
neither
are any
prebuild
or postbuild steps.
The compiler understands many of the directives Microsoft introduced with J++, such as
@dll
,
@com
,
@conditional
, and
#if
. This means you can quickly migrate much of your code that uses J/Direct for accessing native APIs and that implements COM components using JActiveX wrappers. (For more information about using J/Direct and migrating COM components, see Part IV of this book.) Also,
alongside
the JDK libraries, Microsoft has also implemented some of the extension packages that ship with J++, most notably com.ms.com, com.ms.dll, and com.ms.win32. The class libraries include an implementation of the JDBC-ODBC bridge driver in com.ms.jdbc.odbc. This driver requires an ODBC 3.0 or later driver. These packages are all held in the Vjslib.dll assembly. You'll also find an implementation of the WFC libraries in the Vjswfc.dll assembly.
The Profiler, Heap Monitoring, and Debug APIs that were provided with J++ have been omitted from J#. You should use the corresponding .NET Framework Class Library APIs instead.
Converting Class Files
If you do not have the source code for a given set of Java classes available, you can still migrate an application to .NET. Microsoft's Java Binary Converter tool, jbimp, converts
.class
files into MSIL assemblies ”DLLs and EXEs. The simplest way to use jbimp is to run it from the command line. You'll find jbimp.exe in the folder \Program Files\Microsoft Visual J# .NET\Framework\Bin. If you're migrating several class files, you can combine them into a single DLL using the
/target:library
switch, which is similar to that used by the vjc compiler. The name of the DLL is taken from the first class file supplied on the command line, or you can specify a name using the
/out
parameter.
Let's take the
OvenBean,
TemperatureEvent
,
TemperatureEventListener
, and
TemperatureWarning
classes used by the components example earlier in this chapter. (These files are in the JDK OvenBean folder.) Pretend that you've lost the source code and have only the
.class
files created as a result of compiling using the JDK. You can convert them into a .NET DLL using the following command:
jbimp/target:library/out:OvenBean.dllOvenBean.class
TemperatureEvent.classTemperatureEventListener.classTemperatureWarning.class
You can build an EXE using the
/target:exe
parameter (the default), as long as one of the classes has a
main
method. You can also use the
/reference
parameter to link to another .NET assembly. Continuing with the OvenBean example, you can convert the test client (
BeanClient.class
) and link it to the OvenBean.dll assembly by using the following command:
jbimp/reference:OvenBean.dllBeanClient.class
Running this command will result in a .NET executable called BeanClient.exe. (You can use the
/out
parameter to generate an executable with a different name.)
The jbimp tool has a number of other options available, including
/recurse
, which will recursively process
.class
files in subdirectories. This option is useful if you have an entire package that you need to convert.
|