• Tom (unregistered)

    This is a very interesting case that demonstrates why I think java needs to evolve to have better type inferences. I don't know how exactly that can happen, though. In a more dynamic language (Ruby, Python, Javascript), the programmer doesn't have to write all the type decl since type check is only done during runtime. (And the validity of the type should be checked using unit test cases...) On the other hand, in a language with a stronger type system (Haskell etc.), the programmer still doesn't have to write all the type decl since it's inferred at compile time, I think. I wonder if it can be done only because variables are immutable. Either way, maybe it can't be done without changing the java language fundamentally. sigh...

  • - (unregistered) in reply to yuumei

    Well, all the stuff should be split into a real data structure.Thus using a vector of HashMap is a bit strange...

  • (cs) in reply to sf
    sf:
    SomeCoder:
    real_aardvark:
    This seems to be the sanest comment so far (by quite a long way)... ...

    And this seems to be the new sanest comment of the day. I agree 100%.

    Well I think your comment is even more sane still. Bravo! ;-)
    I would second this; but my head is too swollen to read past the other comments.

    You're probably right, though. After I get through sticking a knitting-needle through my frontal lobes, I'll read through the rest of this uninformed lunacy and check up.

  • JavaGuy (unregistered)

    I kinda like java generic declaration syntax

    Class BinarySearchTree<E extends java.lang.Comparable<? super E>>

    I'm sure you can build even more "interesting" generic classes using ? super and ? extends.

  • anonymous (unregistered)

    The only thing missing is a typedef.

  • (cs) in reply to EvanED
    EvanED:
    File file(...);
    try {
       doStuff();
    }
    catch(Throwable t) {
       file.close();
       throw t;
    }
    In your hurry to rant, you got that wrong:
       File file = ...;
       try {
          doStuff();
       } finally {
          file.close();
       }
    If you're going to beat up on a language, at least do it the honour of doing so accurately. Otherwise others will be morally permitted to do the same back to you and everyone ends up looking like dumbasses.
  • (cs) in reply to dkf
    dkf:
    In your hurry to rant, you got that wrong:
       File file = ...;
       try {
          doStuff();
       } finally {
          file.close();
       }
    If you're going to beat up on a language, at least do it the honour of doing so accurately. Otherwise others will be morally permitted to do the same back to you and everyone ends up looking like dumbasses.
    Yeah, I haven't spent enough time in Java recently. I originally posted it without the "throw t", and writing the "catch(Throwable t)" felt weird, but I didn't diagnose the problem right.

    That said, it's still (1) more code that (2) you have to remember every time you use File that (3) is easy omit.

  • Anonymous (unregistered) in reply to Jay
    Jay:
    1. It seems to me that the most obvious protection levels would be "public" and "available to this class and any child classes, but no one else". Indeed I think the latter should be the default. But it's not only not the default, it doesn't even exist. Instead there's the stupid "package" level protection, which makes no sense to me at all. If another class in a package needs access to my ... semi-private ... data, it should have to explicitly say so, not just magically get it.
    The 'protected' keyword is "available to this class and any child classes, but no one else".
  • (cs) in reply to Anonymous
    Anonymous:
    The 'protected' keyword is "available to this class and any child classes, but no one else".
    Not in Java, it isn't. Java 'protected' is actually less restrictive than the default package-level access.
  • (cs) in reply to Michael
    Michael:
    How is it even WTF? Please provide cleaner implementation of multi-dimensional map (by long time and string) with fewer lines of code...
    For cleaner, see my comment a page back. More lines of code, yes, but much cleaner.
  • Alexis (unregistered)

    All your typedef are belong to C++, too bad for you Java guys!

  • Bubba (unregistered) in reply to Jim T
    Jim T:
    It's kind of a shame how all that effort is put into making this very specific data structure, which at the end of the day, stores a load of Objects ...

    This is, indeed, the real wtf.

  • (cs) in reply to EvanED
    EvanED:
    That said, it's still (1) more code that (2) you have to remember every time you use File that (3) is easy omit.
    No dispute there. Mind you, I'm not too keen on the C++ solution either. I'd like to say something like this:
       file.withopen(handle) {
          doStuff(handle);
       };
    and have the language/class look after all the complex stuff for me. (Actually, it makes even more sense with database transactions, where it can also handle things like cancellation on exception for you. Which is really sweet, and an idiom I use in scripting languages a lot.)
  • Yorinaga (unregistered)

    The real WTF is that it should be:

    Map<Long, Map<Timestamp, List<Map<String, Object>>>> superHashMap = new HashMap<Long, Map<Timestamp, List<Map<String, Object>>>>();

    I think this is really only an anti-pattern if your key is something that doesn't implement or inherit its own proper hash function, in that case every insertion/lookup is a collision, and the chaining or whatever goes into N deferments , and you might as well be in a linked list.

  • Thomas Goossens (unregistered) in reply to yuumei

    The real WTF here is the use of Vector, which should obviously be replaced with an ArrayList.

  • (cs)

    In this case C#'s "var" keyword comes in very handy.

  • I (unregistered) in reply to Stilgar
    Stilgar:
    In this case C#'s "var" keyword comes in very handy.

    Also,

    using (FileStream someFileStream = File.Open(@"path\to\file.ext")
    {
      // Use the file!
    }
      // It's now closed, even if u exception!
    
    )

    Works for anything that's IDisposeable.

  • (cs) in reply to dkf
    EvanED:
    I'd like to say something like this:
       file.withopen(handle) {
          doStuff(handle);
       };
    

    and have the language/class look after all the complex stuff for me. (Actually, it makes even more sense with database transactions...)

    Interesting idea. I've always approached it using editor macros to create the appropriate try/catch/finally structure.

    However, I think you can get close to it in Java using a bit of setup and anonymous classes.

    Step 1, put these in your library:

    public abstract class FileAction
    {
        File _f;
    
        public FileAction(File f)
        {
            _f = f;
        }
    
        public File getFile()
        {
            return _f;
        }
    
        public abstract void actOnFile() ;
    }
    
    public class FileActor
    {
        ...
    
        public void withOpen(FileAction fa)
        {
            File f = fa.getFile();
            try
            {
                f.open();
                fa.actOnFile();
            }
            finally
            {
                f.close();
            }
        }
    
        ...
    }
    

    Step 2, use them as follows:

    new FileActor().withOpen(new FileAction(file)
    {
        public void actOnFile()
        {
            // Do your processing here
        }
    });
    

    I'm designing this on-the-fly, so I may have some details wrong. The FileActor and FileAction classes do require a bit of planning and careful coding, but from then on, using them is pretty straightforward and achieves what I think is your goal.

    /rob runs off to code a DatabaseTransactionActor and DatabaseTransactionAction ...

  • James (unregistered) in reply to EvanED
    EvanED:
    And the sorts of things that you want to do like this is vast. Want to temporarily change the value of a variable? In C++, make a restorer class that will restore the old value when destructed. (You only have to do it once, and such a class will come in handy quite a bit.) Want to change the cursor to an hourglass while doing a long operation? Make the destructor turn it back to a pointer.

    So, pretty much you're arguing that Java doesn't let you do opaque, tricky, unmaintainable stuff? Man, that's, uh, terrible.

    Seriously, pretty much none of the stuff you talk about should ever be done in a system that anybody but yourself will ever look at. If you want to "temporarily" change a value, use a named temporary variable. If you want a file closed, close it. If you want to change your cursor after an operation, explicitly call "cursor.Style = Styles::Pointer" or whatever. That way, people know what the hell you were thinking instead of having to grok every line of every class before understanding dawns.

  • distineo (unregistered) in reply to prod

    [quote user="prod"][quote user="Volmarias"]

    "PhoneBook" is much easier to understand than "map< string, string>" (which could mean anything!).[/quote] And a PhoneBook class is what I would have.

  • transverbero (unregistered) in reply to EvanED
    EvanED:
    File file(...);
    catch(Throwable t) {
       file.close();
       throw t;
    }
    :sigh: Finally not in a catch block.

    -- Now on to your point. You can get all this with PhantomRefrences if you must. But yes, Java does not handle ever case in the cleanest way possible.

  • Joakim Arfvidsson (unregistered)

    This is not actually a WTF…

    It is a little unusual to have so many nested collections, but the generic part is totally fine.

  • paratus (unregistered) in reply to EvanED

    [quote user="EvanED"][quote user="dkf"]In your hurry to That said, it's still (1) more code that (2) you have to remember every time you use File that (3) is easy omit.[/quote] Still wrong, Files are just pointers to files, you want a Stream.

    As to forgetting, well in most cases the finalizer will close for you, you just have no idea when. And any Java developer worth shit would be running findbug. Prob Sol.

  • dube (unregistered)

    Maybe we should send him a book about Interfaces so he could refactor it to

    Map<Long, Map<Timestamp, List<Map<String, Object>>>> superHashMap = new HashMap<Long, HashMap<Timestamp, Vector<HashMap<String, Object>>>>();

  • (cs) in reply to James
    James:
    EvanED:
    And the sorts of things that you want to do like this is vast. Want to temporarily change the value of a variable? In C++, make a restorer class that will restore the old value when destructed. (You only have to do it once, and such a class will come in handy quite a bit.) Want to change the cursor to an hourglass while doing a long operation? Make the destructor turn it back to a pointer.

    So, pretty much you're arguing that Java doesn't let you do opaque, tricky, unmaintainable stuff? Man, that's, uh, terrible.

    Seriously, pretty much none of the stuff you talk about should ever be done in a system that anybody but yourself will ever look at. If you want to "temporarily" change a value, use a named temporary variable. If you want a file closed, close it. If you want to change your cursor after an operation, explicitly call "cursor.Style = Styles::Pointer" or whatever. That way, people know what the hell you were thinking instead of having to grok every line of every class before understanding dawns.

    Well, actually, he's talking about transactional semantics, as implemented in an exception-safe fashion via RAII.

    Opaque, no. Java, no. C++, yes (if you're lucky, and you actually have an adult C++ programmer instead of some wacko mutant transplant from the Bio-Chem lab).

    There are ways to handle this in Java, no doubt. This just happens to be the way to do it in C++ -- thus, no need for 'finalize,' just as in Java there is no need for 'typedef.'

    Honestly, this language nit-picking -- it isn't even a real flame war, for goodness' sake -- is plummeting rapidly down the tubes.

  • Magnus Bergmark (unregistered) in reply to dkf
    dkf:
    EvanED:
    That said, it's still (1) more code that (2) you have to remember every time you use File that (3) is easy omit.
    No dispute there. Mind you, I'm not too keen on the C++ solution either. I'd like to say something like this:
       file.withopen(handle) {
          doStuff(handle);
       };
    and have the language/class look after all the complex stuff for me. (Actually, it makes even more sense with database transactions, where it can also handle things like cancellation on exception for you. Which is really sweet, and an idiom I use in scripting languages a lot.)

    Like in Ruby?

    file = File.open('/path/to/file', 'w')
    file << "Hello World"
    file.close
    
    ---------------
    
    File.open('/path/to/file', 'a') do |file|
        file << "Hello World"
    end
    

    Pass a block and you can use the file as much as you want in it, when the block ends, the file will be closed.

    The fun part here is that the blocks are really fancy. They have the potential to blow your minds (LISP-like langs are more hardcore, I've heard)

    class Foo
        # Return a block with one parameter
        def proc
            local_in_foo = 10
            # scope is preserved / saved
            return Proc.new { |f| local_in_foo * f }
        end
    end
    
    saved_proc = Foo.new.proc
    puts saved_proc.call(2) # Outputs "20"
    

    This could be useful for file usage, too. Think of the possibilities! ;-P

  • (cs) in reply to RobFreundlich
    RobFreundlich:
    EvanED:
    I'd like to say something like this:
       file.withopen(handle) {
          doStuff(handle);
       };
    

    and have the language/class look after all the complex stuff for me. (Actually, it makes even more sense with database transactions...)

    Interesting idea. I've always approached it using editor macros to create the appropriate try/catch/finally structure.

    First of all, that quote wasn't me; the part you quoted was dkf.

    Second, editor macros don't help you nearly as much when you're retrofitting code, and depending on how you have it set up, I would guess you still have to remember to use them, and it's still more code that will be sitting around.

    But:

    new FileActor().withOpen(new FileAction(file)
    {
        public void actOnFile()
        {
            // Do your processing here
        }
    });
    
    there is at least one program that uses this convention for all their resources; I think it's Lucine. This has some nice benefits over try/finally (i.e. better enforcement that people actually follow it), but it's still (1) more code than the C# or C++ technique and (2) you can't refer to non-final local variables in the containing function, which may or may not be a serious limitation.
    James:
    So, pretty much you're arguing that Java doesn't let you do opaque, tricky, unmaintainable stuff? Man, that's, uh, terrible.
    I think you'll find plenty of people who agree with me about the merits of RAII techniques.
    Seriously, pretty much none of the stuff you talk about should ever be done in a system that anybody but yourself will ever look at. If you want to "temporarily" change a value, use a named temporary variable. If you want a file closed, close it. If you want to change your cursor after an operation, explicitly call "cursor.Style = Styles::Pointer" or whatever. That way, people *know what the hell you were thinking* instead of having to grok every line of every class before understanding dawns.
    That works fine with C, but once you add exceptions it becomes way harder, because now you have to ensure that any temporary changes get undone even in the face of exceptions.

    IMO, if you're coding C++ and see the line

    scoped_lock lock(mutex);
    and don't realize that the lock is released when it goes out of scope, you're missing a huge benefit of C++. Automatic destructors are, I think, in the running for biggest single improvement over raw C.

    paratus:
    EvanED:
    That said, it's still (1) more code that (2) you have to remember every time you use File that (3) is easy omit.
    Still wrong, Files are just pointers to files, you want a Stream.
    As I said, I'm not a Java programmer. You're concentrating on the specifics, not my actual point, which is that there is something you need to clean up.
    As to forgetting, well in most cases the finalizer will close for you, you just have no idea when.
    Which makes them far, far less useful. Have a lock on something? You can't rely on the finalizer to release that. You need determinism. The cases like this, where you need a resource to be released at a specific point, I would say are far more common than the cases where you just need them released at *some* point. (Even files can get in this situation if you are opening a ton of files.)

    (And it's not just when the finalizer runs, but if; if the garbage collector never runs, the finalizer won't be called. Granted, if this happens it's almost certainly because the program exited, in which case just about any resource I can come up with will be automatically released by the OS or won't matter.)

    And any Java developer worth shit would be running findbug. Prob Sol.
    For a few of these problems. Will it tell me that I forgot to change the cursor back to a pointer?
  • (cs)

    Not a WTF at all in my opinion. Here's a snippet from our code (which I don't consider a WTF either):

      private static Map<Integer,
                         Map<String,
                             Map<Integer,
                                 Collection<StatisticValueInternal>>>> sExtraInfo =
                             new Hashtable<Integer,
                                           Map<String,
                                               Map<Integer,
                                                   Collection<StatisticValueInternal>>>>();
  • Anonymous Cowherd (unregistered)

    The real WTFs are:

    1. Using Vector (synchronized) with HashMap (unsynchronized). He should be using Vector + Hashtable, or ArrayList + HashMap.
    2. Using implementation classes in the generics statement. He should be using List and Map interfaces instead.

    Personally, I would refactor the code so that the Map of Map of List of Map is instead encapsulated into objects in an domain-specific manner.

  • Anonymous Cowherd (unregistered) in reply to ThePants999
    ThePants999:
    Not a WTF at all in my opinion. Here's a snippet from our code (which I don't consider a WTF either):
      private static Map<Integer,
                         Map<String,
                             Map<Integer,
                                 Collection<StatisticValueInternal>>>> sExtraInfo =
                             new Hashtable<Integer,
                                           Map<String,
                                               Map<Integer,
                                                   Collection<StatisticValueInternal>>>>();

    You'd be better off doing either:

    1. Creating an aggregated object that encapsulates the Integer, String, Integer tuple, encapsulating the information in the Map's key, or
    2. Creating a TripleMap, which associates a 3-tuple with a value, thus encapsulating the tuple information within the Map.
  • (cs) in reply to EvanED
    EvanED:
    Automatic destructors are, I think, in the running for biggest single improvement over raw C.
    Lots'o'snip -- sorry. See above for details.

    As you say, automatic -- and in context I think you mean "deterministic" -- destructors are a great advantage. People outside C++ don't seem to realise that this is pretty much nothing to do with memory management (I had no problems tracking down memory management issues in C, before I worked with C++), but with resource management.

    I would, however, humbly take issue with your statement. The biggest single improvement is ... not classes, no no no, and not inheritance and arbitrary levels of protection borrowed, no doubt, from some unholy marriage of Simula and Smalltalk, but ... Templates.

    Luckily, this brings us back to the OP. Stroustrup took a long time to get around to templates in C++, and the syntax is a godawful mess, and he's since apologised. But they work. Extremely well.

    Java's "generics" are an excellent idea, but they're still only syntactic sugar. (Much like 'typedef', in fact. ahem)

    At the end of the day, what you get with Java generics is an Object. What you get with C++ templates is an extremely rich (albeit statically typed) field of design choices and code re-use.

    That is all ye know, and all ye need to know.

  • Mr. RealWTF (TM) (unregistered)

    The real WTF here is the fact that he declared the variable using the concrete classes, plus he is using Vector. Seriously, who uses Vector? It is not 1995 anymore.

    Map<Long, ap<Timestamp, List<Map<String, Object>>>> superHashMap = new HashMap<Long, HashMap<Timestamp, ArrayList<HashMap<String, Object>>>>();

    See? I made it all better.

  • (cs) in reply to Mr. RealWTF (TM)
    Mr. RealWTF (TM):
    The real WTF here is the fact that he declared the variable using the concrete classes, plus he is using Vector. Seriously, who uses Vector? It is not 1995 anymore.

    Map<Long, ap<Timestamp, List<Map<String, Object>>>> superHashMap = new HashMap<Long, HashMap<Timestamp, ArrayList<HashMap<String, Object>>>>();

    See? I made it all better.

    How many times, Dear Lord? How many times?

  • Sud (unregistered) in reply to yuumei

    wish he would have thought about the Blue Pill too...

  • jtv (unregistered) in reply to yuumei

    What's wrong is that after two lines of generics, it's still storing "Object"!

  • (cs) in reply to real_aardvark
    real_aardvark:
    I would, however, humbly take issue with your statement. The biggest single improvement is ... not classes, no no no, and not inheritance and arbitrary levels of protection borrowed, no doubt, from some unholy marriage of Simula and Smalltalk, but ... Templates.
    I think I agree with you, but I would put destructors pretty darn high in the list, probably above classes/inheritance/etc.
    Luckily, this brings us back to the OP. Stroustrup took a long time to get around to templates in C++, and the syntax is a godawful mess, and he's since apologised. But they work. Extremely well.
    I wouldn't say extremely well... they have major problems, like unintelligable error messages. Well see if the new additions to C++0x improve the situation by eliminating or at least lessening some of these weaknesses or if they are too much added complexity.
  • methinks (unregistered) in reply to Rich
    Rich:
    Gah, introducing Vector usage in Java 5? Inexcusable. It's been ArrayList since 1.2 y'know...

    Still wrong, y'know... ArrayList is an implementation, like Vector. The generic Interface for both (!) is List - everything else used in declarations should in fact be treated as a (design) error.

    In declarations all types (i.e. interfaces!) you ever need (and should use) are List, Map/SortedMap, Set/SortedSet, Queue and Collection. Period.

  • methinks (unregistered) in reply to The Java Inquisition
    The Java Inquisition:
    TRWTF is that he coded to the implementation rather than the interface. The line should read:

    Map<Long, Map<Timestamp, List<Map<String, Object>>>> superHashMap = new HashMap<Long, Map<Timestamp, List<Map<String, Object>>>>();

    Much better.

    Also, Vector is deprecated, except that it's not.

    At least one who gets the concept of generic interfaces :o)

    Vector does not have to be deprecated though, as long as it is treated as a List. It is still better to use Collections.synchronizedList(List l) to get a List which is synchronized per-method, but there is nothing wrong in using a Vector in such a case.

    The real error is - as you stated - using Vector (and any other implementation class, at that!) in declarations instead of the generic interfaces.

  • methinks (unregistered) in reply to Robin Message
    Robin Message:
    class PhoneBook extends map<string,string> {}

    It's slightly verbose, but it's basically a typedef. Plus it has the advantage of making the language more orthogonal.

    As this is Java and Map is an interface, it'd be more along the lines of

    class PhoneBook extends AbstractMap<String,String>
    

    but otherwise, 100% d'accord.

  • methinks (unregistered) in reply to EvanED
    EvanED:
    RobFreundlich:
    new FileActor().withOpen(new FileAction(file)
    {
        public void actOnFile()
        {
            // Do your processing here
        }
    });
    
    there is at least one program that uses this convention for all their resources; I think it's Lucine. This has some nice benefits over try/finally (i.e. better enforcement that people actually follow it), but it's still (1) more code than the C# or C++ technique and (2) you can't refer to non-final local variables in the containing function, which may or may not be a serious limitation.

    ad (1): "More code" is never really a reason to abandon a concept when this is the only "issue", especially with modern IDEs.

    ad (2): There is a very good reason for this limitation in Java: Such so-called "(anonymous) local classes" have a very limited scope (i.e. the block of code where there are declared) and can also only see local variables in the same scope. The lifetime of their instances, OTOH, is potentially much longer (the references can be passed out of the enclosing code block, compare the Listener-concept in Swing), so the values of the local variables might not exist any more (on the stack) when such an instance is used. Therefore the compiler stores the value inside a synthetic field in the inner ("local") class.

    As we - the developers - do not see, where the value is copied (it is passed in transparently when the constructor is called BTW) this can only be done unambiguously when the local variable does not change it's value somewhere in said enclosing block, especially after the local class was instantiated.

    Thus it simply has to be final.

    And this is moreover never a limitation: Method parameters can safely be declared final (and should not be "re-used" inside the method body by assigning different values anyway, so one could always declare them final!).

    Most other local variables that change their values are not used inside a local class - and those local variables that have to change their value and be used in a local class (think e.g. loop variables...) can always be copied to a scoped final variable if necessary, e.g.:

    for(int i=0; i<10; i++)
    {
       final fi = i;         // final variable valid in loop body
       new SomeInterface()   // local anonymous class
       {
            // use "fi" here directly... 
            // using "i" yields a compiler error however!
       }
    }
    
  • Rakan (unregistered)

    I like the ignorance of not creating container classes. Even if it might make sense. Sometimes I happen to see code like this on my own. But that is usually an alert for me that some data is screaming to be wrapped in a nice and gentle class. The generics are definitely not the problem. Consider how the code would have worked without it..

  • SomeBloke (unregistered) in reply to Ciaran McCreesh
    Ciaran McCreesh:
    Ah yes. No-one will ever need typedef, so let's remove it from the language! Also, operator overloading is clearly always evil, there are no legitimate uses of multiple inheritance, programmer-controllable memory management is pointless, stack allocation is a waste of time, const is silly, scope-controlled destruction doesn't help anyone, a common base class for everything is good and treating programmers like idiots is the best idea ever.

    Best Comment Ever!!! First thing that made me laugh on this site for a bout 6 months. Alex, feature it! Notwithstanding the comments to MFD, that thing was worth it just to see the creativity of those who parodied it.

    CAPTCHA: secundum, wtf is a secundum?

  • methinks (unregistered) in reply to DKO
    DKO:
    (...) I see Java programmers flaming C++ much more often than C++ programmers flaming Java.
    Do you? I'm surprised. Most (good!) Java devs I know come from a strong C++ background and know exactly what they are talking about when comparing the two. Weak programmers who do not know their stuff flame everything outside their little ecosystem. And many people flaming Java do not know the least about the language, they just read somewhere that it is oh so slow (which was partly true in 1995, yes). You are missing the main point of the omissions (operator overloading, explicit memory management, typedefs, pointer arithmetic etc.) in Java - they are very good to avoid "guru code", that kind of code which is seldom helpful when working on large codebases in an team. It *is* still surprising though how much crap is created in such a comparatively clean language like Java.
    DKO:
    And thank god I don't have to code in Java. It's mostly Python for me. It has typedefs, operator overloading, multiple inheritance, const (through a bit of hacking), scope-controlled deterministic destruction, and no common base class for everything. And it doesn't treat me like an idiot.
    Yes, I like Python too. But you are missing the same point here as above - stuff that can be misused is better left out in a language used for very large systems:

    Multiple inhertitance can be misused much more than the single inheritance/interface concept can (which is not to say that you cannot build crappy data models with that).

    Operator overloading is mainly syntactic sugar and does not add to readability - you never know what a given operator behaves like by just looking at it being used - you have to look at it's implementation too.

    And scope controlled deterministic destruction? Implicit memory management in Java is of course also deterministic - objects going out of scope and not being held on to are destroyed. The fact that many people still manage to create memory leaks in the presence of a GC most of the time can be blamed to a fundamental misunderstanding of OO concepts and bad design.

    The common base class for everything is not in your way if you do not use it (many people do not even know that they derive from Object in Java implicitely when leaving out the "extends"-clause!) - otherwise it is one of the single most important concepts in any OO language: generic data structures and algorithms depend on it heavily (especially in the absence of templates) and with the addition of compile-time safety (the so-called "generics" in Java) this is a good and clean concept. The concrete implementation in Java has its flaws, e.g. the protected clone()-method with the empty Cloneable-interface, a clean mixin-interface would have been much better here. That is to say, you have to really know a language to criticize (not flame!) it.

  • methinks (unregistered) in reply to SomeBloke
    Ciaran McCreesh:
    Ah yes. No-one will ever need typedef, so let's remove it from the language! Also, operator overloading is clearly always evil, there are no legitimate uses of multiple inheritance, programmer-controllable memory management is pointless, stack allocation is a waste of time, const is silly, scope-controlled destruction doesn't help anyone, a common base class for everything is good and treating programmers like idiots is the best idea ever.

    Ah yes. Someone might sometimes think they needed it (but nothing you could not solve with a new class), it is evil most of the time when lowering readability, there are no legitimate uses that cannot be solved with single inheritance/interface (and additionally an association if you really want to share implementation), it is pointless for large distributed systems, point taken for const, a GC is scope controlled, a common base class is a good thing for generic algorithms and data structures and who is treating programmers like idiots?

    You are missing the point here - Java is designed for large distributed systems where you do not care for some CPU cycles on a single machine or an explicitely allocated block of memory where you can perform some nifty pointer tricks.

    If you want to program a 3D game, Java is perhaps not the right choice, you are welcome to use C(++) or whatever at any time.

    For large systems with large teams (with a lot of people who are not on the kernel hacking level) we should be glad that all that stuff was left out, it makes life a lot easier.

  • John (unregistered)

    Can someone explain to me why you would EVER stack hash maps?

  • (cs) in reply to EvanED
    EvanED:
    real_aardvark:
    I would, however, humbly take issue with your statement. The biggest single improvement is ... not classes, no no no, and not inheritance and arbitrary levels of protection borrowed, no doubt, from some unholy marriage of Simula and Smalltalk, but ... Templates.
    I think I agree with you, but I would put destructors pretty darn high in the list, probably above classes/inheritance/etc.
    I agree absolutely, although it's a little difficult to envisage destructors without classes (first-class or not). I think the point we're agreeing on is deterministic destructors, without which I would find it almost impossible to build a large-scale server-side system.
    EvanED:
    real_aardvark:
    Luckily, this brings us back to the OP. Stroustrup took a long time to get around to templates in C++, and the syntax is a godawful mess, and he's since apologised. But they work. Extremely well.
    I wouldn't say extremely well... they have major problems, like unintelligable error messages. Well see if the new additions to C++0x improve the situation by eliminating or at least lessening some of these weaknesses or if they are too much added complexity.
    Well, the unintelligible error messages are largely an artefact of the STL rather than templates themselves. They could be solved either by (a) incorporating the STL into the compiler toolset itself (which would be fine by now, because who the hell ever really needs to drop Dinkumware on top of STLPort, or vice versa?) or (b) supplying a decent filter to interpret the output, much like c++filt does for linker errors.

    Which is by far a bigger WTF for me. Having a separate linker? What on earth is the point, in this day and age?

    I'm looking forward to C++08. None of the ideas are obviously insane, and I'll put up with yet more horrid syntax to be able to use lambda functions instead of my current lovingly hand-tooled functors. The "constraints" thing (or whatever it's called) on templates also looks like a useful design tool.

    I suspect the proof of the pudding is when we look at Java 1.8 and discover how many parts of C++08 it "borrows."

  • (cs) in reply to John
    John:
    Can someone explain to me why you would EVER stack hash maps?
    What would be the point?
  • (cs) in reply to SomeBloke
    SomeBloke:
    Ciaran McCreesh:
    Ah yes. No-one will ever need typedef, so let's remove it from the language! Also, operator overloading is clearly always evil, there are no legitimate uses of multiple inheritance, programmer-controllable memory management is pointless, stack allocation is a waste of time, const is silly, scope-controlled destruction doesn't help anyone, a common base class for everything is good and treating programmers like idiots is the best idea ever.

    Best Comment Ever!!! First thing that made me laugh on this site for a bout 6 months. Alex, feature it! Notwithstanding the comments to MFD, that thing was worth it just to see the creativity of those who parodied it.

    CAPTCHA: secundum, wtf is a secundum?

    Not Fist!!!!

  • (cs) in reply to methinks
    methinks:
    Weak programmers who do not know their stuff flame everything outside their little ecosystem.
    Very true as a general point; less true round these parts.
    methinks:
    You are missing the main point of the omissions (operator overloading, explicit memory management, typedefs, pointer arithmetic etc.) in Java - they are very good to avoid "guru code", that kind of code which is seldom helpful when working on large codebases in an team. It *is* still surprising though how much crap is created in such a comparatively clean language like Java.
    Yes, that is surprising, isn't it? I mean, me, I'm shocked. Particularly after the language designers have spent so much time encouraging "useless dingbat" code and deprecating "guru" code, whatever that might be.

    I'd be interested to hear your arguments as to why Java is a "comparatively clean language." It's a fine language, but it is by no means comparatively clean. Unless, of course, you refer to the syntax -- which is largely irrelevant to anything other than a compiler.

    methinks:
    Multiple inheritance can be misused much more than the single inheritance/interface concept can (which is not to say that you cannot build crappy data models with that).
    Agreed. Stroustrup is wrong on this. I prefer Java's inheritance/interface mechanism by far. And if you want to do multiple dispatch ... well, then, multiple inheritance is not a hell of a lot of use.
    methinks:
    Operator overloading is mainly syntactic sugar and does not add to readability - you never know what a given operator behaves like by just looking at it being used - you have to look at its implementation too.
    An interesting and common objection to operator overloading. But, I think, misguided.

    Operator overloading is clearly not needed in functional languages. In C-derived languages, though, it can be used as a useful design tool. Yes, it's syntactic sugar (and I personally do not like std::string operator+(...) used as 'concatenate'), but it can make things clear through design. As the Greeks say, μηδὲν ἄγαν. (The other classic Greek aphorism, γνῶθι σεαυτόν, is also relevant here.)

    The point is, why deny this syntactic sugar in a language? Why not just castrate the silly bastards who misuse it willy-nilly?

    methinks:
    And scope controlled deterministic destruction? Implicit memory management in Java is of course also deterministic - objects going out of scope and not being held on to are destroyed. The fact that many people still manage to create memory leaks in the presence of a GC most of the time can be blamed to a fundamental misunderstanding of OO concepts and bad design.
    "Of course?" I suspect not. You're talking about an inequality here, not an equality.

    This would be the same definition of "deterministic" that allows some loonies (not you, obviously -- they're normally recruiters) to claim that "Visual Basic can produce a Real Time system."

    Well, no, it can't.

    Well, no, Java memory management is not "deterministic."

    Leaving aside the minor point that Resource Management Is Not Simply Memory Management, it is simply not true to suggest that the Java (or any other) GC will "deterministically" delete free resources. Of course it won't. In a large-scale system, this is potentially a problem. In a large-scale system under stress, this is almost certainly a problem.

    Whilst in no way wishing to suggest that Java is a useless and pointless language that should never have been invented, or should have been strangled at birth before its reinvention from Oak or Green or whatever it was at Sun into "the Language of the Web" (which it subsequently failed to become), I would humbly suggest that other languages do it better, and often without the huge backwash of 'plz send me teh codez' morons.

    Python, Ruby, or some idealised version of Lisp/Scheme spring to mind. Or, going backwards and actually hiring "gurus" (ie sentient human beings, such as you and possibly me) to do your programming for you, C++.

    Java is a very weak half-way house, with a ridiculous and unsustainable amount of superstructure above it.

    I should point out that my current peeve is Python's GIL ... but that's neither here nor there.

  • methinks (unregistered) in reply to Jay
    Jay:
    operator overloading: Amusing and I've had a few cases where it would have been handy. But hardly a big deal. You can always declare a "plus" function and write "c=a.plus(b)" whereever you would like to have written "c=a+b". Unless you're defining a new number type -- complex numbers or fractions or something -- it's likely that operator overloading would subtract from readability rather than add to it.
    I'd even say that op overloading greatly decreases readability and maintainability.
    Jay:
    multiple inheritance: There are occassions where this is useful. Frankly I rarely used it in C++.
    Only if you are *extremely* disciplined and do not introduce contradicting implementations in methods with the same name.
    Jay:
    I've had a few times that I've groaned about having to use "implements" as a poor alternative and write a slew of pass-thru functions. But only a few.
    On the contrary, the interface concept is much cleaner. You can share implementation by associating private helper objects - admittedly, you have to write pass-through methods then, but with modern IDEs this is hardly any additional work, the code can easily be auto generated.
    Jay:
    If you really do need to force-free an object, fine, just set the handle to null.
    Yes.
    Jay:
    If you really care, call gc().
    No. The fact aside that System.gc() is just a hint for the VM, on most occasions calling it by hand does not speed things up, but it can backfire a lot. I once saw a terribly written system which was slow due to extremely bad design. To cater for this, they started to insert gc()-calls every few lines. This, of course, made matters even worse.
    Jay:
    stack allocation: Not sure what you mean there. You can create stacks in Java. Do you mean the the stack that holds your local variables? Why would you want to manage this yourself? It's a trivial operation that the compiler handles just fine.
    Contrary to Java you can allocate objects (i.e. instances) on the stack. The Java "new" keyword always allocates objects on the heap.
    Jay:
    const: Java calls it "final". A dumb name, but it's the same concept.
    Not true. Depending on where you put "const" in the code, it has much broader semantics than "final" in Java. Didn't you say you have programmed in C/C++?
    Jay:
    (...) I have a number of complaints about Java, but they apparently don't overlap your complaints at all.

    My complaints include:

    1. It seems to me that the most obvious protection levels would be "public" and "available to this class and any child classes, but no one else". Indeed I think the latter should be the default. But it's not only not the default, it doesn't even exist. Instead there's the stupid "package" level protection, which makes no sense to me at all. If another class in a package needs access to my ... semi-private ... data, it should have to explicitly say so, not just magically get it.
    You obviously did not get the access modifiers in C++ either. The semantics of "public", "protected" and "private" are exactly the same as in Java, only the default is different: In Java this is the additional "package" visibility, which basically means "public to anyone inside the same package, private to the rest", which BTW is better than the "friend" concept in C++, because it can be applied on a per-field basis. With this visibility you can expose a certain public API from a package, but use lots of helper classes inside the package which are not visible to the outside world. This promotes clean OO design even in the innards of the system (if used correctly of course). Very helpful and a sensible default. If you do not see this, perhaps you have not grasped the package concept as a whole.
    Jay:
    2. No convenient way to pass a primitive by reference or to pass a handle to a handle, like a C++ "&". If a function has to return more than one value, the only way to do this is to create an object to encapsulate the values. Sure, I can do this, but it's tedious. If I need to return, say, name and phone number, why can't I just do it, instead of having to create a new object to contain them.
    By this you are contradicting the paradigms of OO. It is in fact *desirable* that multiple return values should be encapsulated in their own type. Getting the address of a primitive is quite necessary in low level languages like C, but IMHO do not have a legitimate use in OO languages. C++ is only a preprocessor for C, so quite naturally all the older concepts are usable there as well. E.g. pointers are available in C++, but when writing clean OO you are better off with references. Only use pointers if there is absolutely no alternative. The main issue is that most "C++" programs are in fact C programs with a thin layer of OO syntax...

Leave a comment on “Very Specific Generics”

Log In or post as a guest

Replying to comment #:

« Return to Article