Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Comparing Java and C++ - Whitepaper

Comparing Java and C++ - Whitepaper
Really Understanding Java
Myths and Truths Comparing C++ and Java

Whitepaper, 1998
Angelika Langer


 
 

When I got first interested in Java I came across the following statement that George Paolini, Director of Corporate Marketing at JavaSoft, made in an interview that was published in JavaREPORT 5/98:

Q: When will Java win over the C++ developers once and for all ?

A: I think we’re winning them over now. On the programming language side, C++ programmers are probably the most adept at picking up Java. For them it’s basically about leaving out a lot of the trapdoors that were developed in C++ and unlearning a lot of bad habits.

When I later started exploring Java in a little more depth I realised that Mr. Paolini's statements was nothing but marketing hype.

It is true that Java shares many of the syntactical elements with C++, and that indeed makes C++ programmers feel familiar with Java almost immediately. Yet, when you dig deeper you discover that Java is a programming language with its own rules and idioms. Some idioms are common to all object oriented languages, some are similar to rules and idioms in C++, and some are completely Java specific. Just forgetting the complicated stuff from C++ does not imply that you've understood Java. Actually, being a C++ programmer lures you into certain traps. Some of these pitfalls are explored in the following.

Comparison of variables

In Java, the result of applying the operator == to two variables of non-primitive type is true , if the operand values are both null or if they both refer to the same object or array; otherwise, the result is false . Remember that in Java all non-primitive types are reference types, and the operator == checks the identity of both referred objects. In C++, in contrast, the operator == checks for equality of both operands.

The pitfall here is that the syntax of the comparison operator is exactly the same in both languages, yet the meaning is entirely different.

  • In C++, the operator == by default, that is, for built-in types, checks for equality of both operands. This value oriented behavior has become a common idiom in C++ and for this reason even overloaded == operators exhibit the same behavior.
  • In Java, all non-primitive types have reference semantics, and for that reason the operator == checks for identity .
Now, in Java all classes have the method boolean equals(Object o) defined; it is inherited from the base class of all classes, class Object . User-defined classes can override this method in order to implement a functionality that checks for equality . Redefining the equals() function looks like good way to get back the value oriented comparison semantics that we know so well from C++. However, this is treacherous.

The precise meaning of equals() always depends on the specific class and its implementation. equals() must not mean "check for equality "; it can equally well mean anything else. For instance, all classes that do not override the equals() function exhibit the default behaviour inherited from class Object which is "check for identity ". Not even the JDK (the huge standard Java library) defines equals() consistently. Consider the following examples using the string and the string buffer classes from the JDK:

String s = "Hello World !";

String s1 = new String(s);

String s2 = new String(s);

Here, s1.equals(s2) yields true , as expected. If we compare two string variables that both contain the string "Hello World !" then we expect that they compare equal. That's how strings behave in C++, and that's how they behave in Java, too. However, generalising this expectation is misleading in Java. Look at this: StringBuffer sb1 = new StringBuffer(s);

StringBuffer sb2 = new StringBuffer(s);

Now, sb1.equals(sb2) yields false , which we might not have been expecting. After all we compare two sting buffer variables that both contain the string "Hello World !" . My expectation was that they compare equal. But no, they do not! The StringBuffer class does not override the equals() function and for that reason equals() checks for identity of the referred object, and yes, we have two different objects with the same value. So they compare not-equal in Java.

The bottom line is: forget value semantics all together. Don't assume that an operator, such as operator ==, that looks the same in C++ and Java has the same meaning in both languages. Don't believe that Java mechanisms, like the equal() function, would help retain value semantics. It is not consistently used. Lots of pitfalls here! But these are not the only ones.

Assignment. Assignment of variables comes with similar surprises, because assignment is value assignment in C++ and reference assignment in Java. The clone() function again looks like it would get us back value semantics, but by default it does a shallow copy. Again, it depends on each single class what clone() actually means and it is not used consistently.

Reference variables . The type system itself makes for surprises because Java reference variables look like C++ references, but behave like C++ pointers. For instance, in C++ a reference must be initialised to refer to an existing object, and only pointers can have a null value indicating that it does not refer to anything valid. In Java, reference variables are by default initialised with a null reference, which makes them behave pretty much like C++ pointers: you always have to check for the null value before any access.

Construction/destruction . Object construction works differently in both languages. So does object destruction, or its Java counterpart: finalization. It turns out that resource management cannot be reliably done in Java, because you do not know whether a finalizer is invoked at all, and if so when. Even means that were obviously designed to mitigate this problem, like runFinalizersOnExit() have a quite suspicious track record: It did not work in JDK 1.0, it was supposed to work in JDK 1.1, and meanwhile for platform two (JDK 1.2) it is deprecated, because they realised it cannot be reliably implemented.

Polymorphism is different because non-virtual in C++ does not mean final in Java. Function overloading works differently for member functions in an inheritance tree because of the different name lookup rules. The concept of constness in C++ is similar to the semantics of final in Java, yet different. Even exception handling is different in both languages.

So why did Mr. Paolini feel that C++ programmers just have to leave out a lot of the trapdoors that were developed in C++? Java opens quite a number of new trapdoors, especially to C++ programmers. But then, Java would be boring without these challenges, right? ;-)
 
 
 
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
 
Effective Java - Advanced Java Programming Idioms 
5-day seminar (open enrollment and on-site)
 

 

  © Copyright 1995-2003 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/Papers/JavaGotchas/JavaProceedings.htm  last update: 23 Oct 2003