Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 Why Java sucks
Index -> Java
Goto page 1, 2, 3 ... 13, 14, 15  Next
View previous topic Printable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Sat Oct 08, 2005 1:49 pm   Post subject: Why Java sucks

Why does Java suck?

A few notes before I dive in:

Some of these points I care about more than others. They are not necessarily in order of perceived importance.

None of these flaws, by themselves are "deal breakers". I enjoy using languages which make many of these same mistakes. However, when you put all of these together they become overwhelming, in my opinion. Fixing even a few of these flaws would go a long way toward making Java more pleasant to work with.

I believe this document serves an important purpose. There are many programmers who may not have realized that there were alternatives to the way Java does things. If these complaints serve only to point that out, then it has accomplished a great deal.


  1. Where are my lists and arrays?

    Everyone uses lists. We use them everywhere.

    How do you create a list in Java?

    Java:
    ArrayList lst = new ArrayList();
    lst.add(new Integer(42));
    lst.add(new Integer(27));
    lst.add(new Integer(12));


    Things got a little better in Java 1.5.0.

    Java:
    ArrayList<Integer> lst = new ArrayList<Integer>();
    lst.add(42);
    lst.add(27);
    lst.add(12);


    But why can't Java have some kind of literal syntax for lists or arrays? Plenty of other languages do just that and it is most definitely not considered wasteful syntactic sugar.

    code:
    lst = [42, 27, 12]

  2. Why can't functions return multiple values?

    Yeah yeah... in Java they're called "methods".

    Why can't they return multiple values?

    Because C and C++ can't isn't a good reason, but it's the one that we're given. Instead we have to add extra classes to the library, or return a list.

    That second option seems nice, but it means we have to create a list (using the tedious syntax listed above), return it, assign that return value to an array or list variable, then manually grab each element in that list and assign it to some other variable.

    What's wrong with being able to just return more than one value?

    Python:
    def foo():
       return (1, 2, 3)

    a, b, c = foo()

  3. Where are the tuples?

    This one's related to the previous question.

    Simple support for tuples would ease programming tremendously. Where is this concept that so many languages have gotten right? It's missing entirely.
  4. For-each and modification?

    For-each loops make it possible to avoid off-by-one errors when iterating over collections. But this only works when we're reading the value of the elements in the collection.

    If we want to modify those elements, we cannot use the for-each loop.
  5. Where are the lambda functions?

    Instead of:

    Java:
    JButton  helloButton = new JButton("Say Hello");
    helloButton.addActionListener (new ActionListener() {
       public void actionPerformed(ActionEvent ae) {
          System.out.println("Hello");
       }
    });


    Why would it be so awful to allow something like the following?

    code:
    let hello-button :: <button> = make(<button>, text: "Say Hello");
    add-action-listener(hello-button, method (ae :: <action-event>) format-out("Hello\n") end);


    There's no needless naming. Really, does anyone care about "ActionListener" or "actionPerformed"? No, we only care about the fact that we have a method taking one argument that performs some action.

    Yes, the anonymous inner class can provide flexibility, particularly when you want persistent state. However, the vast majority of the time it is complete overkill, and a bad cludge for an important missing feature.
  6. Where are the argument labels?

    Let's say I have a method or constructor which takes several arguments. Which argument does what? Okay, I'll guess. Oops, I got it wrong and the program didn't compile.

    Hmmm... a little editing and voila! It compiles. Now let me run it. Oh... weird... I got some odd logic error. Oh, after half an hour of searching for the bug, it was because I had the right types for the arguments, but i mixed up two of them. Let me go consult the API reference.

    Why do we need to do this?

    Surely there's a better way. Many languages already know this.

    code:
    let win = window ~title:"hello" ~width:400 ~height:300 ()


    Do I need to remember if width or height came first in the argument order? Nope.
  7. Why do methods which take no parameters need parentheses?

    In C and C++, a function's name can be used as a pointer to that function. On its own, it's the address in memory of that function. It makes sense, therefore to have parentheses after the function as an indicator that it's been called.

    Java has no such concept. Nor does it have a delegate concept like D or C#. There is no way to use the name of a method outside of the definition of that method which isn't a method call.

    And yet, we still have to tack on parentheses, even when there's nothing for them to group. It adds noise to a program and forces countless extra keystrokes.

    Perhaps the rational is that, with method overloading, it's impossible to know if a method called without parentheses is a deliberate call without arguments, or a mistake. But in this case, why not assume the programmer knows what he or she is doing?

    While we're at it, why not remove the empty parentheses from method definition? Because it would cause confusion in the syntax?

    Surely there's no other place where we'd use curly brackets directly after a name.

    Java:
    String getName { return this.name; }


    The above can hardly be mistaken for anything other than a method definition.
  8. Why can't property access have syntactic sugar?

    So very many languages get it right that Java's "get*" and "set*" methods are a disgrace. Even the C and C++ derived C# gets this right.

    They add needless syntactic noise to a program and neglect the visual impact of the assignment operator.

    If I see:

    code:
    window.setTitle("hello");


    It certainly takes longer to read that and understand what's going on than it does if I have:

    code:
    window.title = "hello";


    The latter example has no superfluous "set" or parentheses obfuscating the purpose of the statement.
  9. Why can't methods be public by default?

    Certainly most methods in a class are public. Why then do they default to being private?

    Huffman coding principles say the most common code should be the most concise.

    Java:
    class Name
    {
       String first, last;

       Name(String f, String l)
       {
          first = f;
          last  = l;
       }

       String fullName()
       {
          return first + " " + last;
       }
    }


    Certainly that reads better than:

    Java:
    class Name
    {
       String first, last;

       public Name(String f, String l)
       {
          first = f;
          last  = l;
       }

       public String fullName()
       {
          return first + " " + last;
       }
    }


    There's less noise. Less to be confused about.
  10. Why do I need to use "return" so much?

    The "return" keyword is ultimately about control flow. It takes us from any given point in a method and skips right to the end, optionally yielding some value.

    Java:
    String fullName()
    {
       return first + " " + last;
    }


    Now look at this piece of code. Why do I need a "return"? I'm already at the end of the method. There's nowhere else to go.

    Java:
    String fullName()
    {
       first + " " + last;
    }


    There we go. We get to the end of the method, and there's a string. Hey... my method returns a string. Isn't that an incredible coincidence? Maybe my method should return this string.

    Less syntactic noise is good.

    But if I have a more complex method, "return" is clearly necessary, right?

    Java:
    String fullName()
    {
       if (first.equals(""))
       {
          return last;
       }
       else
       {
          return first + " " + last;
       }
    }


    Hmmm... actually, when I look at this, I realize something. The conditional is the last thing in the method. There's nowhere else to go.

    Java:
    String fullName()
    {
       if (first.equals(""))
       {
          last;
       }
       else
       {
          first + " " + last;
       }
    }


    That looks nicer, doesn't it?
  11. While we're prettying things up, what's with the semi-colon?

    The curly brace is a darn effective piece of punctuation. So why don't we use it?

    Java:
    String fullName()
    {
       if (first.equals(""))
       {
          last
       }
       else
       {
          first + " " + last
       }
    }


    Clearly the curly brace indicates the end of the block. The extra semi-colon didn't tell the compiler anything useful.
  12. Multiple methods... same darn thing.

    Now, at some point I'm going to want to override the toString method so my Name class plays nice with the rest of the API.

    I'm already cheating a bit by using my nicer versions of things.

    Java:
    class Name
    {
       String first, last;

       Name(String f, String l)
       {
          first = f;
          last  = l
       }

       String fullName
       {
          if (first.equals(""))
          {
             last
          }
          else
          {
             first + " " + last
          }
       }

       String toString
       {
          fullName
       }
    }


    Even with the prettied up version, I'm still jumping through artificial hoops. Clearly fullName and toString do the same darn thing.

    Java:
    class Name
    {
       String first, last;

       Name(String f, String l)
       {
          first = f;
          last  = l
       }

       String fullName, toString
       {
          if (first.equals(""))
          {
             last
          }
          else
          {
             first + " " + last
          }
       }
    }

  13. Why must parameter types be separately specified?

    So, when I declare variables, I can specify the type once, then a list of variable names.

    And yet, I can't do the same with parameters.

    I can remove more syntactic noise with:

    Java:
    class Name
    {
       String first, last;

       Name(String f, l)
       {
          first = f;
          last  = l
       }

       String fullName, toString
       {
          if (first.equals(""))
          {
             last
          }
          else
          {
             first + " " + last
          }
       }
    }


    There is no conflict, since a subsequent type declaration would start a new set of parameters.
  14. Why must cases fall through?

    The fact that cases fall through in Java is only to maintain syntactic and semantic compatibility with C and C++, which are themselves limited by hardware considerations. Of course, the Java environment doesn't have those limitations, so maintaining compatibility with that restriction is just ridiculous.

    Cases falling through is created to address a lack of another feature. Let's look at a contrived example. We want to return true if a number is 2, 4, 6 or 8, and false otherwise.

    Java:
    switch (number)
    {
       case 2:
       case 4:
       case 6:
       case 8:
          return true;
       default:
          return false;
    }


    Now, let's look at an alternate that poses no syntactic problems.

    Java:
    switch (number)
    {
       case 2, 4, 6, 8:
          return true;
       default:
          return false;
    }


    But since this switch statement doesn't fall through, there's no need for a control flow break to prevent fall-through. So, if this switch is alone in a function, then there's nowhere else the control flow can go, and we can avoid the returns.

    Additionally, we can use the braces as punctuation.

    Java:
    boolean checkNumber(int number)
    {
       switch (number)
       {
          case 2, 4, 6, 8: true;
          default: false
       }
    }


    The syntactic noise is significantly lessened.
  15. Why aren't naming conventions enforced?

    Java, for all of the other nits it has which I can pick, has a decent naming convention.

    That decent naming convention isn't enforced by the compiler.

    There's no good reason for this. It is done only to appease C++ programmers who want to write code that violates the conventions.
  16. How do I create a constant?

    Oh, that's easy.

    Java:
    final int i = 42;


    I can't give a new value to "i", right? So, it's constant.

    Therefore, the following shouldn't compile.

    Java:
    class Foo
    {
       String bar;

       public Foo(String initBar)
       {
          bar = initBar;
       }

       public String getBar()
       {
          return bar;
       }

       public String toString()
       {
          return getBar();
       }

       public void setBar(String newBar)
       {
          bar = newBar;
       }
    }

    public class Test
    {
       public static void main(String[] args)
       {
          final Foo f = new Foo("bar");
          System.out.println(f);
          wooble(f);
          System.out.println(f);
       }

       public static void wooble(Foo f)
       {
          f.setBar("baz");
       }
    }


    This shouldn't compile, right? I mean, "f" is clearly a constant Foo object.

    Yet, it does, and it runs perfectly well.

    You see, the "final" keyword merely means that the variable can't point to another value. This works fine for simple immutable data, like integers, but for an object with mutable state, it doesn't prevent changes to the object at all.

    When we designate data as immutable, we give the compiler extra information. In this case we're telling it to stop us if we try to violate that restriction and make changes to a piece of data. With large APIs, this can be done quite unintentionally.

    But Java offers us no ability to check for such problems when the program is compiled.

    Of course, to make this work, we'd also have to be able to mark methods as being valid to call on constant objects. The other option is to assume that any method which returns "void" has side-effects and thus cannot be called on a constant object.
  17. Non-void methods in a void context?

    That might sound odd, but you've all dealt with this. It's possible to have something like:

    Java:
    f.getBar();


    Which simply returns the "bar" field of the object "f". There's nothing stopping you from having that as-is in a Java program. It serves no purpose, yet the Java compiler does not warn about this or prohibit it.
  18. Java needs a high-priority cast.

    The sheer ugliness of this makes it a point against Java.

    Java:
    ((Foo)bar).baz()


    You'd think we could write:

    Java:
    (Foo)bar.baz()


    ... but that's low-priority, so it translates to:

    Java:
    (Foo)(bar.baz())


    Instead, here's the perfectly reasonable:

    Java:
    (bar as Foo).baz()


    As I've said many times, less syntactic noise is a good thing, and this kind of code is common enough to warrant attention.
  19. Why can't Java do deeper code analysis?


  20. Why is switch limited to integers?

    It's be a piece of cake for "switch" to allow comparison of pretty much any types, using either == for primitive types, or the "equals" method for object types. This could greatly simplify a lot of code, and it'd be relatively easy. It wouldn't even change how switch works currently, but instead just add functionality.

    Why hasn't this been done? Oh, right, to not make C++ programmers feel out of place.
  21. Why can't I write stand-alone functions?

    The "static" context is just silly. It confuses the heck out of many people and accomplishes nothing in terms of code re-use that couldn't be achieved with stand-alone functions and a proper module system, mix-ins or multiple implementation inheritance.

    It makes sense for languages where classes are themselves objects, and are treated as such. In such a language, "static" methods and field are simply non-static fields and methods of the class object.

    Certainly:

    Java:
    void main(String[] args)
    {
       Foo f = new Foo();
       System.out.println(f);
    }

    class Foo
    {
       public String toString()
       {
          return "Hello";
       }
    }


    Makes more sense than:

    Java:
    class Foo
    {
       public static void main(String[] args)
       {
          Foo f = new Foo();
          System.out.println(f);
       }

       public String toString()
       {
          return "Hello";
       }
    }


    Which makes no sense from an OO perspective.
  22. Speaking of mix-ins...

    Why not implement this powerful and simple mechanism for code reuse?

    One of the primary uses of static methods is to provide a class full of utility methods for use on various types of objects which all implement some interface. Just look at the Collections class for an example.

    Quote:
    This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends.


    By and large, these methods take a single collection of some sort (Lists, Sets, etc.) as their first argument. I know I've seen this pattern before...

    Oh yes, in Python, where the "self" object is passed as the first argument to every member function in a class. These member functions act as methods.

    So it's not too hard to see something like:

    Java:
    Collections.sort(someList);


    As:

    Java:
    someList.sort();


    Much nicer, eh?

    Except, of course, "sort" isn't a method of the List class, so that won't work.

    Let's look at the way it works in another language. In this case, Ruby.

    I want to be able to sort anything that implements a particular means of iterating over its contents (let's call this an "interface"), but I don't want to have to write all of the code for each of these classes, especially since I know it's going to be the same for each and every one of them. It simply depends on that iteration scheme, and nothing else about the state of the object.

    So I have a class Array. Array objects have an "each" method which handles the iteration. I then "mix-in" the module Enumerable, and among other things, I get the implementation of a "sort" method.

    What about not wanting to add those methods to an entire class? Just want them added for a single object? That's easy enough. The result is singleton.

    Even C#, not the world's most innovative language, is implementing mix-ins, including singleton support, though Microsoft prefers to call them "extension classes".

    Why has Java ignored such a source of power? Why does the language rob its users of such power?

    There can be no good reason for such a profound lack.
  23. What happened to compile-time code analysis?

    Clearly, at compile-time we can look at the following and realize that only one String object has to be created. Since Strings are immutable, there's no harm in this.

    Yet, via the "==" operator, which tests for referential equality, we can see that two different String objects have been created containing the exact same contents.

    Java:
    class Test
    {
       static String foo = "hello world";
       static String bar = " ";
       static String baz = "hello" + bar + "world";

       public static void main(String[] args)
       {
          if (foo == baz)
             System.out.println("true");
          else
             System.out.println("false");
       }
    }


    The fact that this level of simple compile-time analysis doesn't happen is troubling.

    Of course, if we write the following, it shows that they do point to the same object.

    Java:
    class Test
    {
       static String foo = "hello world";
       static String baz = "hello world";

       public static void main(String[] args)
       {
          if (foo == baz)
             System.out.println("true");
          else
             System.out.println("false");
       }
    }


    Thus, essentially what's happening is that the compiler is waiting until run-time to perform the string concatenation that could easily have been done at compile-time.
  24. Why can't we do some type-inferencing?

    C#, a language which shares much with Java in terms of design, is gaining a limited form of type-inferencing. Why doesn't Java employ such capabilities to make programming easier?

    Compare:

    Java:
    BufferedReader keyboard = new BufferedReader(
       new InputStreamReader(System.in));


    With:

    Java:
    var keyboard = new BufferedReader(
       new InputStreamReader(System.in));


    The compiler can easily figure out at compile-time that "keyboard" is a BufferedReader instance. Why should I have to tell it explicitly?
Sponsor
Sponsor
Sponsor
sponsor
rizzix




PostPosted: Sat Oct 08, 2005 5:39 pm   Post subject: (No subject)

i hope you're feeling better now... Laughing

for the most part i'd agree with most of the stuff... but with a few exceptions:

#1) the syntax you proposed as an alternative approach in some cases was really ugly (it just dosent feel right, but yea i know it can be improved)

#2) Java's switch statement does integers __and__ Enums

#3) lists are unnecessary.. you can just as well use the Arrays.asList() function. It serves the purpose.

#4) instead of implementing mix-ins.. i believe it would be best to implement AOP directly into the language.

#5) i don't like the way c++ implements immutability (i,e a special keyword)... infact i believe it is best that annotations are used to represent that as well as the other things such as the access restriction of methods classes etc (i.e public, protected, so on..), after all all that falls under the category of Meta-data anyways.

#6) none of those arguments above justify for the amount of bashing that java has received from you,, here in compsci ... Laughing
wtd




PostPosted: Sat Oct 08, 2005 5:50 pm   Post subject: (No subject)

rizzix wrote:
#3) lists are unnecessary.. you can just as well use the Arrays.asList() function. It serves the purpose.


Well, I can deal with backing it as arrays, lists, arraylists... whatever, but some literal form would be nice.
wtd




PostPosted: Sat Oct 08, 2005 5:59 pm   Post subject: (No subject)

rizzix wrote:
#4) instead of implementing mix-ins.. i believe it would be best to implement AOP directly into the language.


Aspect-oriented programming is a somewhat ambiguous term.

Perhaps some type inferencing (deeper than what I described in my original post) is what you're looking for?

Kind of Ruby-esque duck-typing, but enforced at compile-time.

You may wish to look at O'Caml for an example of this combined with objects.

code:
$ ocaml
        Objective Caml version 3.08.2

# let foo bar = print_endline bar#baz;;
val foo : < baz : string; .. > -> unit = <fun>
# class wooble = object end;;
class wooble : object  end
# foo (new wooble);;
This expression has type wooble but is here used with type
  < baz : string; .. >
Only the second object type has a method baz
# class ninja = object method baz = "hello" end;;
class ninja : object method baz : string end
# foo (new ninja);;
hello
- : unit = ()
#
wtd




PostPosted: Sat Oct 08, 2005 7:52 pm   Post subject: (No subject)

rizzix wrote:
#5) i don't like the way c++ implements immutability (i,e a special keyword)


Ironically the same keyword that Java has reserved but unused. Wink

And at least C++ has immutability. Smile
wtd




PostPosted: Sat Oct 08, 2005 9:29 pm   Post subject: (No subject)

rizzix, I'd like to thank you for the reasonable response. I was hoping leaving out particularly hot issues like performance and operator overloading would encourage that kind of response. Smile
wtd




PostPosted: Sat Oct 08, 2005 9:58 pm   Post subject: (No subject)

Let me add another thing to wish Java had.

No, it's not C++ preprocessor directives, but it does pretty much the same thing at a much higher level.

D's "static if" allows for compile-time decisions.
rizzix




PostPosted: Sun Oct 09, 2005 12:48 am   Post subject: (No subject)

actually by AOP i was refering to AspectJ. i just think it should be an integrated part of java.
wtd




PostPosted: Sun Oct 09, 2005 1:11 am   Post subject: (No subject)

I can see the DbC stuff being useful, but I would make it an integral part of the language. To additionally encourage the use of such a technique, I'd make it as easy as possible.

I think that perhaps forcing programmers to write out an "if" statement to evaluate a condition and throw an exception is making them work too hard. Consider Eiffel as an example of what I'm thinking of.

code:
foo(x : INTEGER) is
   require
      arg_too_small : x >= 42
   do
      std_output.put_integer(x)
      std_output.put_new_line
   end


Certainly debugging aspects (no pun intended) of it are important as well. I've always liked D's "debug" keyword which just makes it so easy.

code:
debug
{
   writefln("We're debugging!");
}

debug (4) // we can have numbers to represent different debugging levels
   writefln("We can have the debug line apply to just one statement");

debug (hello):
   // we can have string labels for controlling debug blocks,
   // and the debug section can extend to the end of the current block
wtd




PostPosted: Sun Oct 09, 2005 1:16 am   Post subject: (No subject)

For wrapping methods with pre and post actions... the pattern matching capabilities of AspectJ are interesting. Without them it'd certainly be tedious to have the same pre or post action for several different methods.
wtd




PostPosted: Sun Oct 09, 2005 1:18 am   Post subject: (No subject)

I see little widespread future support for something like AspectJ, though, unless they dramatically improve the documentation. It's decent enough, but they go way overboard on the buzzwords. PHBs may love it, but programmers won't.
wtd




PostPosted: Sun Oct 09, 2005 1:58 am   Post subject: (No subject)

Some Python code to tease your AOP-oriented mind. Smile

code:
>>> def pre(pre_f):
...    def decorate(f):
...       def x(*args, **kwds):
...          pre_f()
...          f(*args, **kwds)
...       x.__name__ = f.__name__
...       return x
...    return decorate
...
>>> def yo(): print "yo"
...
>>> @pre(yo)
... def hello(): print "hello"
...
>>> hello()
yo
hello
>>> hello.__name__
'hello'
>>>
wtd




PostPosted: Sun Oct 09, 2005 2:12 am   Post subject: (No subject)

Also fun. Smile

code:
>>> def require(*types):
...    def decorate(f):
...       def x(*args, **kwds):
...          for n, (type_name, arg) in enumerate(zip(types, args)):
...             if not isinstance(arg, type_name):
...                raise TypeError("Type mismatch on arg %d.  Should be %s but was %s." % (n + 1, str(type_name), str(type(arg))))
...          return f(*args, **kwds)
...       x.__name__ = f.__name__
...       return x
...    return decorate
...
>>> @require(int, int)
... def foo(a, b):
...    return a + b
...
>>> foo("hello", "foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 6, in x
TypeError: Type mismatch on arg 1.  Should be <type 'int'> but was <type 'str'>.
>>> foo(1, 2)
3
>>>
Naveg




PostPosted: Sun Oct 09, 2005 12:27 pm   Post subject: (No subject)

wtd wrote:
leaving out particularly hot issues like performance and operator overloading


I don't completely understand this, but I think it talks about how poor java performance is an abused myth in some respects. Take a look.\

http://www-128.ibm.com/developerworks/java/library/j-jtp09275.html?ca=dgr-lnxw01JavaUrbanLegends
wtd




PostPosted: Sun Oct 09, 2005 12:31 pm   Post subject: (No subject)

Yes, it does.

That's why I chose not to complain about Java's performance. I think there's enough to complain about with respect to the language, without touching the JVM.
Display posts from previous:   
   Index -> Java
View previous topic Tell A FriendPrintable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 15  [ 225 Posts ]
Goto page 1, 2, 3 ... 13, 14, 15  Next
Jump to:   


Style:  
Search: