notes-computer-programming-programmingLanguageDesign-prosAndCons-java

" 5) Method overloading. One of the most constraining parts of the Java language specification is method resolution, where the compiler has to work out what method you intended to call. Resolution touches superclasses, interfaces, varargs, generics, boxing and primitives. It is a very complex algorithm, that is difficult to extend. Having no method overloading, except by default parameters, would be a huge win.

6) Generics. Java generics is complex, especially around the ? extends and ? super variance clauses. Its too easy to get these wrong. The lesson from Java should be that use-site variance hasn't worked out well. " -- purported mistakes in Java, from http://blog.joda.org/2010/09/next-big-jvm-language_964.html

" steve21 September 2010 14:45

Fix Java and you end up with Scala. It's as simple as that.

The thing you imagine has already been done. Go through "Java Puzzlers" one-by-one and check if these bugs/design mistakes still exist in Scala (hint: most of them are fixed, while some are maintained to be more compatible with Scala).

1) Checked exceptions. Fixed.

2) Primitives and Arrays. Fixed.

3) Everything is a monitor. Every reference type in Scala inherits from java.lang.Object for compatibility reasons. Java has to fix it.

4) Static. Fixed, but creates static forwarders for java compatibility.

5) Method overloading. The JVM has this hard-coded. InvokeDynamic? might allow languages to do method resolution on their own. Java compatibility.

6) Generics. Fixed. Won't get better without full reification. http://lamp.epfl.ch/~emir/bqbase/2006/10/16/erasure.html The problem is that there is no VM currently which can handle all necessary aspects of it. The JVM doesn't even know Generics yet and the .NET VM can hardly handle co-/contravariance.

As you see, most complexity stems from the fact that Scala tries to be as Java-friendly as possible. Some things which would have been easily possible (like classes only different in their arities, like in C#) haven't been done, because the necessary name mangling wouldn't have looked good from java.

Let's go on...

C-like syntax: Yes!

Static typing: Absolutely.

OOP with functional elements: That's Scala's definition.

Easy access reflection: Scala devs are currently writing their own, because Java's isn't good enough.

Properties: Yes.

Closures: Yes.

Null-handling: Yes, via Option, which is more correct and general than the "Elvis" operator. Nulls still supported due to Java compatibility.

Concurrency story: A whole truckload of them for every purpose. Look at Akka as an exmaple or the implementation of the new parallel collections.

Modules: Yes.

Tools: The Scala compiler has a whole API which IDEs can use instead of implementing things themselves. Work is being done to improve all three major IDEs: IntelliJ?, Netbeans and Eclipse. SBT is currently the best dependency management and build system available.

Extensibility: Yes, Implicits.

If any language will be able to unseat Java (which I don't expect) it will be Scala.

Actually, Scala is far easier than Java. Seeing all these mediocre applications and libraries in Java, it is clear that Java is too complex for the average programmer. Reply "

--

" List-oriented programming

This is the default in Lisp, Haskell, and most functional languages. It also happens to be the default in Ruby and (to some extent) Perl; not so much in Python, oddly enough.

It's almost exactly like collection-oriented programming, with the following essential differences:

    By default, operations on collections do not have side-effects. If you make a change to a collection, you get a copy of the collection back, unless you specifically use the destructive version of the operation. Most languages (Haskell being the notable exception) provide destructive versions.
    The language always has first-class functions, so there's typically a standard suite of higher-order functions applicable to all collections: collect, enumerate, filter, findMin, findMax, dropWhile, takeWhile, and so on. The best-known and possibly best-designed set of higher-order functions can be found in the Haskell Prelude
    Operations on collections are (almost) always composable (Lisp screws it up somewhat with the stupid-arsed distinction between lists and returning multiple values) — this is the benefit of every operation returning a collection as a value, rather doing than what Java does and returning either void or perhaps a single value.

Why is any of this useful?

Well, composing functions can yield pretty compact, readable code. My favorite example is from Ruby — let's say you want to get a sorted list of all the methods on class String that actually change the contents of the string; in other words, the destructive operations. Each language has its own convention for how to name the destructive variants (except for Common Lisp, which has no such convention, just a bunch of crappy old unintuitive names). In Ruby, the convention is to end the name with "!". In Ruby, you just say:

irb(main):001:0> "".methods.sort.grep(/!/)

> ["capitalize!", "chomp!", "chop!", "delete!", "downcase!",

"gsub!", "lstrip!", "next!", "reverse!", "rstrip!", "slice!", "squeeze!", "strip!", "sub!", "succ!", "swapcase!", "tr!", "tr_s!", "upcase!"]

Nice. If you're a Java programmer, an interesting exercise is to write this in Java. Since Strings are immutable in Java, assume you're looking for all String-class methods that contain the case-insensitive substring "index" (e.g. all the indexOf/lastIndexOf variants, six in all).

I'd be surprised if you could do it in fewer than 10 lines of code, and it would be ugly enough to make a grown man cry. In fact, I'm going to do it, just 'cuz I want to make this point crystal clear. Here's one way to do it:

	List results = new ArrayList();
	Method[] methods = String.class.getMethods();
	for (int i = 0; i < methods.length; i++) {
	    Method m = methods[i];
	    if (m.getName().toLowerCase().indexOf("index") != -1) {
		results.add(m.getName());
	    }
	}
	String[] names = (String[]) results.toArray();
	Arrays.sort(names);
	return names;

Most ways to do it in Java look pretty much like this.

The code above may not look so ugly in isolation, but next to the Ruby version, it's downright hideous. Part of this is because unlike higher-level languages, Java has no syntax for reflection; it's all done through a (very) clunky OO interface. This particular function doesn't look so bad, but when it comes time to start invoking methods reflectively, it gets even worse.

But there are a lot of little things going on here, all idiomatic Java style, that add up to create the relative ugliness of the code above:

    You have to explicitly allocate the result list. Languages with higher-order functions automatically allocate a collection to hold the results of your visitor function. They provide ways to do it yourself, if you need to, but the default is to do it for you.
    You have to use an iterator (in this case, a for-loop, because Java primitive arrays are non-orthogonal to Java Collections, and lack OO APIs.) to grub through the methods, looking for matches.
    Simple string comparisons are awkward in Java (and what's with the magic number -1, anyway? Jeez.), let alone regexp comparisons. Although to be fair, regexp matching got a little easier in Java 5, when they added some methods to String class to match regexps, bypassing the otherwise clunky OOP interface you normally use.
    Collections don't have a sort() method in Java! (Ack!) List does not, which "sort of" makes sense, since you wouldn't want to inadvertently try sorting a linked list or doubly-linked list (or worse, a circular list). So you have to manually convert the results to an array, which still (even in Java 5) requires an unsafe cast.
    Arrays.sort(), which is already a poor substitute for an actual sort function on actual arrays, doesn't return the sorted array. It returns void. So you have to return it on a separate line.

This might not seem like a big deal, but the Ruby code was an order of magnitude smaller than the equivalent Java code. When you scale it up, you wind up with 10 million lines of Java for every million lines of Ruby.

What about C++? In C++, you simply cannot do it, not in any portable way. A "

--

" waps 1 day ago

link

But there are several things java insists on that are going to cost you in performance in java that are very, very difficult to fix.

1) UTF16 strings. Ever notice how sticking to byte[] arrays (which is a pain in the ass) can double performance in java ? C++ supports everything by default. Latin1, UTF-8, UTF-16, UTF-32, ... with sane defaults, and supports the full set of string operations on all of them. I have a program that caches a lot of string data. The java version is complete, but uses >10G of memory, where the C++ version storing the same data uses <3G.

2) Pointers everywhere. Pointers, pointers and yet more pointers, and more than that still. So datastructures in java will never match their equivalent in C++ in lookup speeds. Plus, in C++ you can do intrusive datastructures (not pretty, but works), which really wipe the floor with Java's structures. If you intend to store objects with lots of subobjects, this will bit you. As this wasn't bad enough java objects feel the need to store metadata, whereas C++ objects pretty much are what you declared them to be (the overhead comes from malloc, not from the language), unless you declared virtual member functions, in which case there's one pointer in there. In Java, it may (Sadly) be worth it to not have one object contain another, but rather copy all fields from the contained object into the parent object. You lose the benefits of typing (esp. since using an interface for this will eliminate your gains), but it does accelerate things by keeping both things together in memory.

3) Startup time. It's much improved in java 6, and again in java 7, but it's nowhere near C++ startup time.

4) Getting in and out of java is expensive. (Whereas in C++, jumping from one application into a .dll or a .so is about as expensive as a virtual method call)

5) Bounds checks. On every single non-primitive memory access at least one bounds check is done. This is insane. "int[5] a; a[3] = 2;" is 2 assembly instructions in C++, almost 20 in java. More importantly, it's one memory access in C++, it's 2 in java (and that's ignoring the fact that java writes type information into the object too, if that were counted, it'd be far worse). Java still hasn't picked up on Coq's tricks (you prove, mathematically, what the bounds of a loop variable are, then you try to prove the array is at least that big. If that succeeds -> no bounds checks).

6) Memory usage, in general. I believe this is mostly a consequence of 1) and 2), but in general java apps use a crapload more memory than their C++ equivalents (normal programs, written by normal programmers)

7) You can't do things like "mmap this file and return me an array of ComplicatedObject?[]" instances.

But yes, in raw number performance, avoiding all the above problems, java does match C++. There actually are (contrived) cases where java will beat C++. Normal C++ that is. In C++ you can write self-modifying code that can do the same optimizations a JIT can do, and can ignore safety (after proving to yourself what you're doing is actually safe, of course).

Of course java has the big advantage of having fewer surprises. But over time I tend to work on programs making this evolution : python/perl/matlab/mathematica -> java -> C++. Each transition will yield at least a factor 2 difference in performance, often more. Surprisingly the "java" phase tends to be the phase where new features are implemented, cause you can't beat Java's refactoring tools.

Pyton/Mathematica have the advantage that you can express many algorithms as an expression chain, which is really, really fast to change. "Get the results from database query X", get out fields x,y, and z, compare with other array this-and-that, sort the result, and get me the grouped counts of field b, and graph me a histogram of the result -> 1 or 2 lines of (not very readable) code. When designing a new program from scratch, you wouldn't believe how much time this saves. IPython notebook FTW !

reply

upvote

PaulHoule? 9 hours ago

link

Hadoop and the latest version of Lucene come with alternative implementations of strings that avoid the UTF16 tax.

Second, I've seen companies fall behind the competition because they had a tangled up C++ codebase with 1.5 hour compiles and code nobody really understand.

The trouble I see with Python, Mathematica and such is that people end up with a bunch of twisty little scripts that all look alike, you get no code reuse, nobody can figure out how to use each other's scripts, etc.

I've been working on making my Java frameworks more fluent because I can write maintainable code in Java and skip the 80% of the work to get the last 20% of the way there with scripts..

reply "

--

" The downside of C++ exceptions is you can’t tell (without the source and the impetus to check) if any function you call may throw an exception. In addition to worrying about resource leaks and destructors, you have to worry about RAII and transactional semantics to ensure your methods are exception safe in case they are somewhere on the call stack when an exception is thrown. In solving one problem, C++ created another.

So the designers of Java sat down, stroked their beards and decided that the problem was not exceptions themselves, but the fact that they could be thrown without notice; hence Java has checked exceptions. You can’t throw an exception inside a method without annotating that method’s signature to indicate you may do so, and you can’t call a method that may throw an exception without wrapping it in code to handle the potential exception. Via the magic of compile time bondage and discipline the error problem is solved, right?

This is about the time I enter the story, the early millennium, circa Java 1.4. I agreed then, as I do now, that the Java way of checked exceptions was more civilised, safer, than the C++ way. I don’t think I was the only one. Because exceptions were now safe, developers started to explore their limits. There were coroutine systems built using exceptions, and at least one XML parsing library I know of used exceptions as a control flow technique. It’s commonplace for established Java webapps to disgorge screenfuls of exceptions, dutifully logged with their call stack, on startup. Java exceptions ceased to be exceptional at all, they became commonplace. They are used from everything from the benign to the catastrophic, differentiating between the severity of exceptions falls to the caller of the function.

If that wasn’t bad enough, not all Java exceptions are checked, subclasses of java.Error and java.RuntimeException? are unchecked. You don’t need to declare them, just throw them. This probably started out as a good idea, null references and array subscript errors are now simple to implement in the runtime, but at the same time because every exception Java extends java.Exception any piece of code can catch it, even if it makes little sense to do so, leading to patterns like

catch (e Exception) { ignore }

So, Java mostly solved the C++ unchecked exception problem, and introduced a whole slew of its own. However I argue Java didn’t solve the actual problem, the problem that C++ didn’t solve either. The problem of how to signal to caller of your function that something went wrong. "

--

" Sun Microsystems had the right people to make Java into a first-class language, and I believe it was the Sun marketing people who rushed the thing out before it should have gotten out. They made it impossible for the Sun software people to do what needed to be done. " -- http://queue.acm.org/detail.cfm?id=1039523

--

" Now just to mention a couple of things about Java: it really doesn’t have a full meta-system. It has always had the problem—for a variety of reasons—of having two regimes, not one regime. It has things that aren’t objects, and it has things that it calls objects. It has real difficulty in being dynamic. It has a garbage collector. So what? Those have been around for a long time. But it’s not that great at adding to itself.

For many years, the development kits for Java were done in C++. That is a telling thing.

We looked at Java very closely in 1995 when we were starting on a major set of implementations, just because it’s a lot of work to do a viable language kernel. The thing we liked least about Java was the way it was implemented. It had this old idea, which has never worked, of having a set of paper specs, having to implement the VM (virtual machine) to the paper specs, and then having benchmarks that try to validate what you’ve just implemented—and that has never resulted in a completely compatible system.

"

--

"

upvote

pjmlp 45 days ago

link

Java generics are an example on how not to do it, but it does not mean all generic implementations are like Java's.


pjmlp 44 days ago

link

--

" LeafStorm? 9 hours ago

link

Working with basic data structures like maps, sets, and lists that you don't have to manually allocate takes approximately 20 times as much code as it does in nicer languages like Python. "

--

From http://loadcode.blogspot.com/2009/12/go-vs-java.html "[Git] is known to be very fast. It is written in C. A Java version JGit was made. It was considerably slower. Handling of memory and lack of unsigned types [were] some of the important reasons." Shawn O. Pearce wrote on the git mailing list: "JGit struggles with not having an efficient way to represent a SHA-1. C can just say "unsigned char[20]" and have it inline into the container's memory allocation. A byte[20] in Java will cost an *additional* 16 bytes of memory, and be slower to access because the bytes themselves are in a different area of memory from the container object."

---

"In the field of programming languages two fresh starts have been attempted recently. I refer to Java and C#. Both tend to restrict rather than augment the number of features, trying to suggest a certain programming discipline. Hence they move in the right direction. This is encouraging. But there remains still a long way to reach Pascal, and longer even to Oberon." -- http://www.swissdelphicenter.ch/en/niklauswirth.php

--

" So we worked hard to make sure that data andimplementation were separate, but that the data couldhave tags that say, “I’m a bag of bytes that’s under-stood by this type.” And if the client doesn’t under-stand the component’s data type, it would be able toturn around and say, “Gee Mr. Server, do you havethe implementation for this particular type?” andreach out across the Internet, grab the implementa-tion, and do some checking to make sure that it won’tturn the disk drive into a puddle of ash. " -- http://dfjug.org/thefeelofjava.pdf

--

" For example, “used-before-set”is afatal compilation error rather than just a warning.These may feel like restrictions, but it’s rare that thecompiler gives an error message without a very goodreason. In all cases, we would try something and seehow many bugs came out of the woodwork.

One of the interesting cases was name hiding. It’spretty traditional in languages to allow nested scopesto have names that are the same as names in the outerscope, and that’s certainly the way it was in Java earlyon. But people had nasty experiences where they for-got they named a variableiin an outer scope, thendeclared aniin an inner scope thinking they werereferring to the outer scope, and it would get caughtby the inner scope. So we disallowed name hiding,and it was amazing how many errors were elimi-nated. Granted, it is sometimes an aggravation, butstatistically speaking, people get burned a lot bydoing that, and we’re trying to avoid people gettingburned. "

" Another thing that’s essential for reliability, oddlyenough, is garbage collection. "

" Pointer restrictions

This also relates to restrictions on pointers andpointer arithmetic, which can lead to interfaceintegrity problems "

" Exception handling

The exception model that we picked up pretty muchstraight out of Modula 3 has been, I think, a real suc-cess. "

" Dynamic linking

Another important aspect of Java is that it’sdynamic. Dynamic linking—where classes come inand have their links snapped very late—lets you adaptto change. Change not only in the versioning prob-lem from one generation of software to the next, butalso in the sense of being able to load handlers fornew data types. It lets the system defer a lot of deci-sions—principally object layout—to the runtime.

We had a longstanding debate, particularly withpeople from the Objective C crowd, about factoriesversus constructors. A factory is a static method on atype—that is, you would saytype.newrather thannew Type. I was not totally persuaded by the fac-tory argument because there was always the problemof who, in the end, creates the object. So Java stayedwith the C++ way of sayingnew Type.

But factories are used as a style in places where youdon’t know exactly what you want or if you need anew object. If you want a font, for example, you don’tnecessarily want to create it and might prefer to lookit up. Java’s dynamic behavior is often fed by this styleof using static factory methods to allocate objectsrather than call the constructor directly.

One way this is used is in this short cliché of doingnewon a string name, where you start by calling astatic method on a class calledforName, which takesthe string as a parameter and gives you a class objectthat happens to have that name. Where this becomesinteresting is when the string parameter toforNameis something that you compute by concatenatingstrings together. You use a method on a class objectcalledmakeInstance, which calls the default con-structor:

Class c = Class.forName(“foo.”+x); Thing b = (Thing) c.makeInstance();

These two things together interact with these objectsin Java called class loaders, which are responsible fortaking a class name and finding an actual class imple-mentation for it. This is the central cliché for achiev-ing this quanta of behavior where we take the MIMEtype, for instance, do a little bit of string mushing toturn the MIME type into a class name, and sayClass.forName, which causes all sorts of HTTPsearches to happen. And then magically you’ve got ahandler for that type that’s been installed dynamically.

Another stylized way we use this is for adapters.Adapters are interfaces that are designed for achievingportability. Say you have an interface to some net-working abstraction or file system, and you want toprovide both a consistent superclass with a consistentinterface and an appropriate subclass that is dependentupon the actual operating system you’re using. Adapterobjects are often looked up by using a factory ratherthan a normal constructor. We then use the previouscliché fed by a property inquiry like system.getPropertyof os.name, as shown in Figure 1, which returns a stringlike Solaris or Win32. If you’re trying to find an imple-mentation of this abstract class Spam and you have aSpamSolaris, a SpamWin?32, and a SpamMac? class, youcan go through this sequence to obtain the appropriate“spamming” class for your machine. And it ends upbeing completely portable. "

" Much of Java’s semantics and that of the virtualmachine were driven by a couple of canonical exam-ples. Namely, I wanted to get a = b + c to be oneinstruction, and p.m of something to be about three instructions. It currently tends to be four or five, whichis still a pretty small number. That goal pretty muchdictated that the system be statically typed. Many lan-guages infer a fair amount of this type informationor they’ll compile it, assume a type, and then dochecks, but that adds a huge amount of complexity.Strong typing simplifies the translation processtremendously and picks up a lot of programmingerrors as well "

" So, how does Java feel? Java feels playful andflexible. You can build things with it that arethemselves flexible. Java feels deterministic. Youfeel like it’s going to do what you ask it to do. It feelsfairly nonthreatening in that you can just try some-thing and you’ll quickly get an error message if it’scrazy. It feels pretty rich. We tried hard to have a fairlylarge class library straight out of the box. By andlarge, it feels like you can just sit down and writecode."

--

http://blog.plover.com/prog/Java.html

--

pron 13 hours ago

link

Well, I like Go, and it certainly has its use cases. But I personally worked on a hard-realtime safety critical mission Java project (if the program gave the wrong answer people could die, and if it gave the right answer late, tens of millions of dollars would be lost). We couldn't have done that without Java's concurrent data-structures (which Go doesn't have), without Real-Time Java's hard realtime guarantees, and it would have been very cumbersome without Java's brand of dynamic linking.

So sometimes Go is good enough, but sometimes you need the full power of the JVM.

reply

--

http://blog.paralleluniverse.co/2014/05/01/modern-java/

http://blog.paralleluniverse.co/2014/05/08/modern-java-pt2/

--

https://en.wikipedia.org/wiki/Comparison_of_C_Sharp_and_Java

--

rwallace 13 hours ago

link

I found to my surprise that these days Java is as productive as anything else. It's not like the early days anymore; modern Java has generics, reflection, now lambda; there would still be more finger typing than I'd ideally like, but the IDEs are more than good enough to make up for that, to the point that a nontrivial program now usually takes fewer keystrokes to write in Java than almost any other language.

Which of course reinforces your main point: nowadays the language differences just aren't that big.

reply

--

in "Growing a Language" by Guy L. Steele Jr, Steele said he would like to add to Java generics and operator overloading and light-weight types that remain on the stack ("whose objects can be cloned at will with no harm"), because these would allow the language to be extended and to let the extensions look just like the base language (e.g. for complex numbers and matrixes), and as a lower priority, tail calls and ways to read and write machine flags for floating point numbers.


simonw 3 days ago

link

I think C makes a great second language. Use Python to teach if statements, variables, control structures etc - then later use C to teach low-level algorithms, pointers and how computers actually work.

I don't think there's any harm in learning Python first. I think they may be harm in learning C first - it's much more likely to frustrate people and potentially even put them off programming.

This is the same reason I dislike Java as a teaching language: having to tell people "don't worry about what public static void main(String[] args) means just yet" when teaching "hello world" isn't a great introduction to programming.

reply


https://en.wikipedia.org/wiki/Criticism_of_Java


http://stackoverflow.com/questions/4011260/sandbox-jvm-to-secure-server-from-untrusted-sources