This is a long post, but illustrates some of the Pros and Cons of Ada vs Java in a practical, industrial computing sense. Executive Summary: An example is shown of how something trivially easy in Ada can be a real bear in Java. The same example shows the extensive technical support for Java that is available to overcome these difficulties, and should make all readers on this list ponder the fact that there's no Ada equivalent. (Typical example: How to parse XML into Ada, How to generate HTML in Ada, How to implement a TCP/IP stack in Ada etc) I wrote in a previous post: > Now I like Java. After Ada, it's my favourite language, and I often find myself wishing "gee, I wish Ada could do X as quickly and easily as I can with Java 1.whatever". (Of course just as often, I wish that Java of any variety could do Y at all, when Ada-95 does it trivially, and Java suffers from fatal flaws due to its C ancestry, and .... but that's beside the point). Example of the type of thing I mean: Java Solution - From Latest JDC Tech Tips: --------QUOTE START ------------------------- MAKING DEFENSIVE COPIES OF OBJECTS Suppose that you are doing some graphical programming using the Rectangle class in the AWT. In your program, a Rectangle object is passed to a constructor for a class. The constructor checks the width and height of the Rectangle to make sure that the values are positive. If width and height are positive, then the volume of the rectangle (width * height) is guaranteed to be positive. In the program, you also take other steps to ensure that objects of your class reference only valid Rectangles. For example, you make the class final. In this way, no subclass can invalidate the error checking you do in the program. The code looks like this: import java.awt.Rectangle; final class TestRect { private final Rectangle rect; public TestRect(Rectangle r) { // check for width/height at least 1 if (r.width < 1 || r.height < 1) { throw new IllegalArgumentException(); } rect = r; } public int getVolume() { return rect.width * rect.height; } } public class CopyDemo1 { public static void main(String args[]) { // create a Rectangle and a TestRect Rectangle r = new Rectangle(0, 0, 5, 10); TestRect t = new TestRect(r); // set width to an invalid value and // compute volume //r.width = -59; System.out.println("Volume = " + t.getVolume()); } } So is this code foolproof? Can a bad Rectangle object somehow get through the checks and be referenced from within TestRect code? The answer is yes. If you uncomment the line in the CopyDemo1 program that reads "r.width = -59", and then run the program, the result is: Volume = -590 which is probably not what you had in mind. The problem is that when you pass an object as an argument to a method, you're really passing a reference (pointer) to the object. This means that a single object can be shared across various parts of a program. See the December 5, 2000 Tech Tip "Returning Multiple Values From a Method" for a further explanation of how arguments are passed to a method. How can you fix the problem illustrated above? One way is to make a copy of the Rectangle passed to TestRect: import java.awt.Rectangle; final class TestRect { private final Rectangle rect; public TestRect(Rectangle r) { // use copy constructor to copy Rectangle // object rect = new Rectangle(r); // check for valid width/height if (rect.width < 1 || rect.height < 1) { throw new IllegalArgumentException(); } } public int getVolume() { return rect.width * rect.height; } } public class CopyDemo2 { public static void main(String args[]) { // create Rectangle and TestRect objects Rectangle r = new Rectangle(0, 0, 5, 10); TestRect t = new TestRect(r); // set width to an invalid value r.width = -59; // compute volume System.out.println("Volume = " + t.getVolume()); } } In this approach, you make a copy using the copy constructor for Rectangle, the constructor that takes a Rectangle argument and creates a new object with the same values. You make the copy before validity checking. The validity checking is done on the copy to avoid subtle problems in multi-threaded programs. One of these subtle problems is the possibility that another thread could change the passed-in argument after it has been checked but before it has been copied. If you run the CopyDemo2 program, the result is: Volume = 50 Because you made a copy of the argument within the constructor, changing the width to -59 outside of the constructor has no effect on the result. Note that making copies in this way has some cost in speed and space, especially if you copy big objects. Another way to apparently solve this problem is to use a clone() method. But this approach doesn't always work, as the following example illustrates: import java.awt.Rectangle; final class TestRect { private final Rectangle rect; public TestRect(Rectangle r) { // clone the Rectangle object rect = (Rectangle)r.clone(); // check for valid width/height if (rect.width < 1 || rect.height < 1) { throw new IllegalArgumentException(); } } public int getVolume() { return rect.width * rect.height; } } // subclass of Rectangle with bogus clone() method class MyRectangle extends Rectangle { public MyRectangle(int x, int y, int w, int h) { super(x, y, w, h); } public Object clone() { return this; } } public class CopyDemo3 { public static void main(String args[]) { // create MyRectangle and TestRect objects Rectangle r = new MyRectangle(0, 0, 5, 10); TestRect t = new TestRect(r); // set width to an invalid value r.width = -59; // compute volume System.out.println("Volume = " + t.getVolume()); } } Rectangle is not a final class, so it can be subclassed. In this example, the class MyRectangle is defined with a degenerate clone method, one that simply returns a reference to the object being cloned. A clone method like this could either be malicious or just poorly written. In either case, you don't get the desired result. The output of the CopyDemo3 program is the same as the CopyDemo1 program: Volume = -590 The problem of shared access to objects can show up in other contexts. Suppose you add an accessor method to TestRect that returns the Rectangle reference stored within the object. A user can then change the object in an arbitrary way, and the assumptions you made about the object's validity no longer hold. Another way of fixing the problem is to pass the width and height values as integers, and not pass in a Rectangle object reference. In general, you can also fix the problem by making a class immutable, that is, objects of the class cannot be changed after creation. But Rectangle is mutable, and so you need to find another way to solve the problem. Another situation where you might want to make a defensive copy is illustrated in the following program: class TestNames { public static final String names[] = {"red", "green", "blue"}; } public class CopyDemo4 { public static void main(String args[]) { TestNames.names[0] = "purple"; System.out.println(TestNames.names[0]); } } The fact that the names array is final means that you can't assign to it. But you can still change the value of a particular array slot. When you run this program, you get the result: purple In other words, you have effectively overwritten what is supposed to be a read-only array. How do you solve this problem? One way is to copy the array using clone, another is to create a read-only view of the array using the Collections Framework. Here's an illustration that shows both approaches: import java.util.*; final class TestNames { private static final String names[] = {"red", "green", "blue"}; // return a copy of the names array public static String[] getNames() { //return names; return (String[])names.clone(); } // return a read-only List view of the array public static List getNamesList() { return Collections.unmodifiableList( Arrays.asList(names)); } } public class CopyDemo5 { public static void main(String args[]) { // attempt to modify names[0] and then // print its value TestNames.getNames()[0] = "purple"; System.out.println(TestNames.getNames()[0]); // get names array as a read-only list and // print the value in the first slot List list = TestNames.getNamesList(); System.out.println(list.get(0)); // attempt to modify the read-only list //list.set(0, "purple"); } } Notice that this program makes the names array private, and defines an accessor method for it, getNames(). However, simply going this far does not solve the problem. You can still change the first slot in the array. So the program clones the array. After the cloning, if you change the first slot of the array, it only affects the copy. Of course, cloning a large array has a speed and space cost attached to it. Notice too the Collections Framework mechanisms used in the program. Arrays.asList() creates a list backed by an array. Collections.unmodifiableList then creates a read-only view of the list. The array is not copied in this case, but instead a read-only List view is built on top of it. These solutions involve either a shallow copy or no copy of the underlying array. If the array contains mutable objects, then there is still a problem because errant user code can change the value of an individual object within the array. In the CopyDemo5 program above, an array of strings is used, and String objects are immutable. The need for defensive copying also shows up in other contexts, such as after an object has been deserialized, or when an object is added to a Set or Map. For example, one of the properties of a set is that it contains no duplicate elements. What if a mutable object is added to a set, and then it later changes so that equals() is true between that object and another one in the set? At this point, the set is corrupt. To avoid this problem, a defensive copy should have been made. For more information about making defensive copies of objects, see item 24 "Make defensive copies when needed" in "Effective Java Programming Language Guide" by Joshua Bloch (http://java.sun.com/docs/books/effective/). ----- QUOTE END ----------------- Ada Solution: Use a non-access-type parameter of mode "in". Thus: TestRect(R : in Rectangle); Discussion: Parameters of mode "in" may be read, but not written to. They can be treated in most respects as constants. If the parameter is an access type, then the contents of the object being accessed may be changed by other tasks, but the access itself can't be, it still references the same object. ------------------------------------------------------