Javapocalypse
jz13.java.no
jz13.java.no
Creating the UI was easy enough, but making an HTTP response was hell. The simplest HTTP GET took around 30 lines of code, and the compiler kept complaining about unhandled exceptions (I know the URL is fine, it's a fixed string I can see, it's not going to throw MalformedURLException, I don't want to catch it, thank you. What? I can't compile without it?). Wrapping the calls up in try/catch blocks was easy enough (if a bit verbose), but now the variable only existed in that scope.
I know I'm new enough that I can't form an opinion about the language, but I know that something that has as simple an interface as an HTTP GET call shouldn't take 30 lines of boilerplate to do. I'm wondering how people deal with this issue (I'm guessing it's a lack of a good standard library?). What attracts people to the Java world? Is it the tooling (Eclipse was, admittedly, very good about auto-fixing my mistakes). Is the verbosity just "the way it is", or am I doing something wrong?
Portability, security (automatic memory, static typing, etc.): you cannot bore a hole in your feet when shoving a nail. And backed by a huge company. Relatively fast. More reasons there: http://c2.com/cgi/wiki?WhyJavaIsGreat
>exception-related problems
A common hack it to wrap your code in a dumb try/catch block at the uppermost level. That way there is no scope-related problem and the compiler won't complain. Ofc that should only been done in a situation such as yours, not in "real life" (i.e. "entreprise world" for java).
About the try/catch, it sounds like something that'll come back to bite me in the ass, so it's a bit unfortunate that I have to pick between being verbose and handling exceptions that can never occur, or being terse and hacky...
In Java, I have to handle malformed URL exceptions, even though I can see the URL in front of me, and if it's not malformed the first time, it'll never be. I see your point about runtime vs compile time, but my issue is with mandatory vs optional handling. I guess you can add "throws MalformedURLException" to let it bubble up, which is kind of the same thing, though (although you have to add all the exceptions you don't want to handle there, in that case).
(I'm a Python dev and hate Java)
The good thing is - newer java libraries use unchecked exceptions in the vast majority of their APIs.
A desktop top app is a good example of this. Network connection gone, disk full, file missing, etc. These things should not be fatal. Once the user gets back within WiFi range, empties their recycle bin, or plugs in some external storage device, the code should be able to continue sensibly.
In this situation, handling RuntimeExceptions at a high level loses all the context. Whereas, remembering to handle all the RuntimeExceptions every time you make a call is silly - they should be checked exceptions then the compiler can warn you. I've ended up wrapping a Java library to make sure that I got checked exceptions to avoid this problem.
Of course, some methods in Java throw checked exceptions some throw RuntimeExceptions. Most of these choices are sensible, but sometimes you have a hard-coded URL and you know it's not malformed. But equally, sometimes you have to catch a specific RuntimeException because that is a situation that you want to handle.
That's a very good point. Malformed URL would mean that there's an error in the program. It's not an exceptional situation, like e.g. user entered malformed URL into a text field.
In these cases you should simply throw AssertionError immediately. But that still results in unnecessarily verbose code. Ideally, there should be two constructors, one that would throw an unchecked exception (URL.create(...)) and one that would throw a checked exception (URL.createOrThrow(...)). (You can create an unchecked constructor yourself.)
try {
*your stuff*
} catch (WhateverYouDontWantToHandleException e) {
throw new RuntimeException(e);
}
Then at the outmost level of your code somewhere you catch RuntimeException and tell the user a general error occurred or something. You will still get the root exception so you can log with stack trace and eventually implement some specific exception handling for it if necessary.A RuntimeException, and any subclass of it, is unchecked and so the compiler does not force you to handle it.
IMHO, go was right to get rid of exceptions (stack traces are useful) and just return two values.
Unfortunately its extreme object-orientation, lack of type inference and polymorphism, lack of first-class functions and anonymous functions, and awkward error handling, make it quite painful to actually write (at least without code completion and automatic refactoring), while encouraging a number of poor design choices and serious source code bloat. It's too high-level to be useful for systems or game programming, but it's not expressive enough to be very productive for programming at a higher level of abstraction, and it's useless for scripting. So, it's mostly used for building enterprise web services. And Android apps.
Like Smalltalk, Eiffel, C#, VB.NET
> It's too high-level to be useful for systems or game programming
True if you are speaking about AAA games or writing kernels, for everything else lots of people seem to be quite successful using it.
> So, it's mostly used for building enterprise web services. And Android apps.
And in embedded environments powerful enough to run it, like windmills, electricity control meters, missile radar controls, blue ray players, J2ME mobiles, ...
But C# does have closures and lambdas, and lets you use functions as arguments and return values. Java doesn't have any of those yet, although they're supposed to be coming in 1.8.
All primitive types are actually alias for classes and structs that live in the System namespace.
My point was basically from a point of view of someone with compiler development background, if one bashes Java for being OO based, there are lots of other languages to bash for the same sin, which for whatever reason people like to forget.
Most modern languages are all OO based in some form, while being more flexible, like your example.
Java 8 will probably improve the situation. At least on the enterprise level, I don't see it moving away from Java and .NET anytime soon.
Polymorphism = the ability to write one function that works for multiple types. So for example, if you want to implement a sorting algorithm, you can write one function that is capable of sorting ints, floats, doubles, chars, and Strings. Java doesn't have this, so if you want to sort four different types, you have to write four functions instead of one, using overloading (you give all four functions the same name but they accept and return different types).
If you're interested in these topics I recommend learning a language like Scala, SML, OCaml, or Haskell. It will open your eyes to some of the very powerful features of higher-level and functional programming languages.
Yes, the Java 1.5+ generic data structure classes that take parameterized types allow a sort of polymorphism, but only for objects (including wrapper classes), but it's not quite as powerful as functional polymorphism.
For example, in SML you can swap an ordered pair of any two datatypes with one line of code:
fun swap(x: 'a, y: 'b): 'b * 'a = (y,x)
And to illustrate my other point about type inference, this is also legal SML and does the same thing. You can leave the type annotations off and the compiler will figure them out for you:
fun swap(x,y)= (y,x)
where x and y can each be any datatype.
I don't really know Scala but from reading about it I get the impression that it also allows this.
Have fun doing it in Java though... you'll have to implement your own generic ordered pair class.
Of course, that doesn't work for int, float, etc because those types aren't objects, but then you typically don't have an array of those.
In Java, with the exception of String[], you would normally only use an array with primitives. If you're working with objects you would want to use a different data structure such as a linked list.
Java is very capable of writing parametrically polymorphic functions — on object types. They are called Generics, in Java-Parlance. Primitive types are excluded from that, but as you've already pointed out above, one can write ad-hoc polymorphic functions (methods.)
Finally, Java has subtype polymorphism — that kind of polymorphism that allows you to write a `Comparable` interface and have your class implement it, so the generic sorting function will work.
So in a sense, Java has "more" polymorphism than most other languages: ad-hoc, subtype, and polymorphic. With the exception of primitive types, for which only ad-hoc polymorphism works. Arrays are another icky area, you should avoid using them with Generics (parametric polymorphism) if at all possible. Generally, you would want to use primitive types and arrays in performance-critical areas of your code. Ideally, those shouldn't be many, so in practice, I found that to be a non-issue.
What is an issue is that Generics (parametric polymorphism) are a bit weird in Java-land, mostly because of type-erasure, their poor interaction with Arrays, and weird stuff with static and/or child classes.[1]
BTW, Haskell only has (bounded) parametric polymorphism. The "bounded" here is actually pretty important. The languages you mention implement some variant of System F as their type system. Haskell has System FC, and IIRC, OCaml has System F_ω. Java's Generics were inspired by Haskell's type system (Phil Wadler was one of the driving forces behind Java's generics.)
[1] Note: type erasure isn't a problem per se. Haskell has type erasure, and it's all well and good. The problem really comes from mixing type-erased generics with subytping and dynamic casts. That's just a mess.
Ad-hoc polymorphism = overloading functions or methods within a class
Subtype polymorphism = overloading methods from the superclass.
Java is fine for working with objects, but it's just a terrible language for working with primitive datatypes. Part of the reason it's not really used for performance-critical code.
Don't confuse the two:
Bounded = Ad-hoc = Haskell type-classes.
Parametric = Universal = unconstrained type variables.
E.g. of parametric polymorphism: fst :: (a,b) -> a
E.g. of bounded polymorphism: show :: Show a -> String
I plead with you to check out http://jmonkeyengine.org and see for yourself
The only reason why large shops are attracted to the Java world is because the endless supply of low cost developers and a tooling ecosystem which helps such developers survive in that ecosystem.
That is the No 1 reason every manager I've know has ever given for using Java.
How does dependency injection throw type safety out the window? You can only inject types that are a subtype of the declared type. Overridden methods can't extend the types of checked exceptions that are thrown [1]. I fail to see the hole in the type system here.
I'm not a Java programmer, but I use dependency injection all the time in other language, including dynamic languages like Ruby. I find it very helpful for isolating bits of code and making extendable interfaces. I'm genuinely curious if there's a major weakness lurking in DI that I haven't discovered yet. Please help me remove my blinders.
[1] http://stackoverflow.com/questions/5875414/method-overriding...
I've been bitten by this in the past with SpringMVC. It usually ripples up quickly and has never been an issue in a released app.
My mental model for dependency injection is a little different. I think of dependency injection as a technique instead of a tool. The only tool I used to do DI is a text editor.
Here's an example of how I would use dependency injection (manually) in Java:
interface Dependency {
void doSomething();
}
class Foo {
Foo(Dependency x) { ... }
}
new Foo(new ConcreteDependencyA());
new Foo(new ConcreteDependencyB());
Both instances of Foo receive their dependencies at runtime, but the compiler is going to check that (1) the concrete dependencies are subtypes of the abstract dependency, (2) the concrete dependencies don't throw extra checked exceptions in their doSomething() method, and (3) the dependent class (Foo) only uses the interface of the abstract dependency. I gain the advantages of dependency injection without undermining the language's type safety.Sure it can: http://en.wikipedia.org/wiki/Dependency_injection#Manually_i... http://www.martinfowler.com/articles/injection.html#Construc...
> because you're not injecting them, you're passing them to the constructor
Injection simply means that the dependency is sent from outside the class. That's exactly what's happening in the constructor example.
> The problem with this model is that you're polluting your constructors with non-functional arguments
I don't consider it "pollution." Who says that IDependency is non-functional? I don't think I've ever injected a non-functional dependency.
> if you want to change one of these, you have to change all your constructor calls!
This is a red herring. If you have to change all your calls, you failed to make your code DRY. That's the programmer's fault. Use a factory or default arguments.
95% of the time, the reason for injecting a dependency is that you want to use a fake implementation in your unit tests, but a particular concrete example in your production code. Have the default constructor setup the concrete dependency and use the extra constructor for your unit tests. This works most of the time for me.
interface IDependency {
void doSomething();
}
class Foo {
Foo(IDependency) { ... } // for unit tests
Foo() { this(new ConcreteDependency()); } // for production
}
Doing DI manually requires a little bit of skill, but not much. I actually find that it teaches design principles more than it requires apriori knowledge of them. Having never used a configuration-based DI tool, it wouldn't be fair for me to conclude with any comparison between the approaches. However, given that this conversation started when you complained that DI tools subvert a static type system, I'm inclined to believe that the DI tools introduce accidental complexities that outweigh their benefits in the simplest use cases.Logging is the classic example. Or the data service provider, or CXF-type stuff
> Use a factory or default arguments
I think that's the main point. Framework-managed DI puts a stop to the factory madness that you always ended up with back in the old Java or C++ world.
> Have the default constructor setup the concrete dependency and use the extra constructor for your unit tests
This will work for a while, but when you start needing to test complex flows, you're going to end up having classes calling classes calling classes, having multiple constructors will become unmanageable.
> DI tools introduce accidental complexities that outweigh their benefits in the simplest use cases
I totally, fully, definitely agree with you! The heavy tools such as Spring or EE6 are ugly, frustrating behemoths with absolutely no advantages for small or even medium developments. But as soon as it starts becoming a 40MY+ project I find you can't avoid them :(
cf. Guice, which is compile-time checked:
https://code.google.com/p/google-guice/wiki/SpringComparison
I had a somewhat long discussion with a colleague about this over the past year, and we looked at all the error cases in the C standard library and came to the conclusion the Java API was probably throwing checked exceptions in one or two corner cases where it should throw unchecked runtime exceptions. I'd be curious what other people think about this. I think specifically, I was asking him (a more Sr. engineer who has used Java heavily and considers it basically good) why closing an IO stream in a finally block throws another checked exception.
In short, I don't think you're doing anything wrong.
[0] See this, but I should caveat this with saying it's a bad API as designed to be consumed by an application. http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLCon...
[1] http://developer.android.com/reference/org/apache/http/clien...
In the end, I used this excellent library: http://loopj.com/android-async-http/
It's asynchronous, but it still required the work to be done in an AsyncTask, which is odd to me (why spawn a thread to spawn another thread?), but it wouldn't work in the main thread, and Android was good enough to tell me about it.
I'm glad that I'm not doing anything wrong, but I'm also sad because I was hoping I was doing something wrong. Thanks for your reply!
http://android-developers.blogspot.com/2011/09/androids-http...
I think that checked exceptions are a failed experiment in language design. They basically force you to leak abstractions all over your code, reintroducing a lot of the pain of error codes.
public String setCache(String key, String value) throws CacheUnavailableException,CacheWriteException {
try {
...
} catch (SocketException e) {
throw new CacheUnavailableException(e);
} catch (SomeMemcacheException e) {
throw new CacheWriteException(e);
}
}
This will allow library clients to catch exceptions that mean something to them, not exceptions that only make sense if you know how the library is using memcache.I agree checked exceptions are annoying, though I have kind of grown to like them since they force me to think about failure cases.
You're going to hate this answer, but honestly, after you do Java for awhile the lines of code just become noise you don't notice... it's like reading and writing XML by hand; kinda sucks, but eventually you just stop noticing.
I should read about checked exceptions more, I don't currently understand the difference between checked and unchecked.
I don't hate that answer, I was just hoping it wouldn't come to that :P Getting used to a language to get one's job done is fine, but it's not something that would cause one to fall in love with said language...
You probably want to have at least one exception "catch" block somewhere, such as within a message/event handler loop, but checked exceptions force you to write the error catch at the place where a routine is called, and MANY such checks for other calls, rather than at one place at the top of an operation.
While this might make sense in a GUI/AWT/Swing app, it does not make sense in a batch processing app (or web app). In that case, there is no user to get a dialog box, you simply log "it did not work due to ..." and move on. Checked exceptions make you write a bunch of extra code.
I will definitely consider it when I know a bit more about it, though, as it seems a much saner language.
For exceptions, there are two types. Checked exceptions and runtime exceptions.
* Checked exceptions need to be explicitly handled in your code, or it doesn't compile. * Runtime exceptions can just happen, you don't need to check them. I personally don't really like the runtime exceptions because they can hide in thousands of place and throw when you don't expect it. However, you don't need to check them so it makes for less verbose code... but it's like putting your head in the sand.
To reduce verbosity of exception handling, I find a pseudo-solution is to just propagate the exception up the stack and deal with it later. It depends for what exception, but for instance, a MalformedUrlException for which you KNOW the Url is fine, can be checked much higher in the call stack. You will save yourself the local verbosity, which hides meaning, and really if the Url is malformed, you might not want the app to keep running; if it's hard-coded you should expect it to be right. It's in the same spirit of Go's `regexp.MustCompile(string)` that makes a compile error if the hardcoded string you give it is not a valid regex.
That being said, Java is a daily pain, but the extensive tooling makes it usable.
So somewhere up in your class, you'd write:
private final static URL someUrl;
static {
try {
someUrl = new URL("http://something.com/");
} catch(SomeException e) {
crashAndLogSomehow(e);
}
}
You would thus know right when you load your app, before distributing it, whether an exception can happen. In your code, you wouldn't have the try/catch block hiding the real meaning.What happens if a static block throws an exception is that the class will not be loaded, and every other time the code refers to the class it will get a ClassDefNotFoundException, which can lead to a 'fun' time tracing the actual issue. Hence why static blocks are not used very much (though it can be convenient).
You can't check at compile time that the Url is valid, but if as soon as you load the program, it crashes with this exception, it will be hard to ignore and the mistake won't be hidden. Since you catch the exception that's thrown a static time, there's no ClassDefNotFoundException.
See examples of this concept in Go:
http://golang.org/pkg/regexp/#MustCompile http://golang.org/pkg/text/template/#Must
The better solution would be to have a compile time error, but I don't think Java can do that.
If I am writing live code, I...want to write code. I don't want something to do it for me. If its in a language I am comfortable with I can generally write it more effectively than a tool in a toolbox and know how to fix it when it breaks.
Maybe I am just a control freak.
The Java language, while I was using it, was very slow to change or evolve. Tools, on the other hand, didn't suffer from the bike-shedding and lack of leadership, so they evolved quickly to solve the pragmatic problems they could solve. When I decided to look around and choose a language to re-invest myself in I looked very closely at leadership.. Who the BDFL was (and whether one existed), how the language was evolving, and what the values of the community were. That meant more to me than syntax, though of course they're related.
Sort of like "watch the player, not the ball" advice you get in soccer.
Java 7 also lacks very useful features like lambdas, mixins, nested methods etc.
However, Java is a good choice for many projects, because it has fantastic tooling, lots of libraries and it's relatively fast. The tooling is really a huge advantage of Java. When I refactor Java code in Eclipse, I sometimes wonder how would I do this when it was Python (which I use for smaller projects).
I wish there was a language combining the pros of Java & Python...
Dart is closest to my ideal of a general purpose language.
It remains to be seen if the Go language will grow significantly from one major release to the next, and how frequent any such changes will be. (it might stagnate, as well)
Sun / Oracle have demonstrated the last 10 years that they do not intend to improve the Java language itself (COBOL with separate compilation, user defined types, field accessors and subclassing is good enough for all!), but will only keep rolling out new "Enterprise" libraries / frameworks.
One area in Java that is still incredibly annoying, however, is the ubiquitous getter/setter pattern that bloats all Java code for zero benefit to anyone.
The main goal behind the standard Java library (from what I have gathered) has always been to provide a mid-level API to most possible functionality in an OS so that library developers can develop simple Java libraries that work across all platforms supported by Java. The big changes to the JRE libraries in the early years of Java was really what caused all of the big problems Java has had. Since the JRE stopped evolving, (server side) Java has really thrived with most code being very easily deployable anywhere and everywhere in the enterprise.
If you're going to program in Java, you should use libraries for commonplace tasks, and you should use an IDE to autocomplete package names and 'run' blocks. If you do this, Java really does work very well. For huge classes of coding tasks, the excessive boilerplate and compile time checks / exceptions can make it possible to tell very quickly if code is correct or not even without tests which is a major boon for code review purposes in enterprise.
Ultimately what I'm trying to say is that Java really does work very well and has come a very long way in creating a very productive environment for large projects, and it's why Java is so popular and heavily used even when at first glance it looks terrible.
Using an IDE in Java is almost a necessity. With that, people mostly just get use to it.
I recently had to return briefly to Java (also for android). All I had to do was recursively chmod a directory. This is something that once seemed simple to do in Java, and indeed it is still simple. However now when I write that code I am painfully aware of how unnecessarily long and complicate it is. However, the larger project involve cross compiling python with c-extensions (and external c dependencies), so I do appreciate the relaxation that comes with Java boilerplate.
For new developers that want to do more than trivial things in Java, reading "Effective Java" will help you avoid many of the pitfalls in the language.
As an example, the current best practice around exceptions is that checked exceptions were a mistake. Don't use them in new code. For code that must deal with checked exceptions, you can create your own general exception class derived from RuntimeException, then use a try/catch block to throw your own runtime exception that wraps the checked exception. (But do this smartly; for cases like IOException with web stuff, it is sometimes easier to add "throws IOException" to all your related methods than to exhaustively catch and rethrow.)
As a second example, do not use the JDBC library directly. It is very poorly designed and direct usage can very easily lead to resource leaks. Use something like Spring's JDBC templates or roll your own. (I generally find Spring to be a bloated mess, but the JDBC template stuff is very useful.)
But perhaps the best bet is to use a more modern JVM-based language (of which my favorite is Clojure), though use of these alternatives on Android may be problematic.
You're not alone. I find checked exceptions to be an invaluable language feature.
Checked exceptions bugged me as well when I was first introduced to Java. I honestly still don't like the fact that exception handling is enforced at the compiler level. It feels intrusive.
I did write a short blog post on the subject, which may be helpful. As a disclaimer, I'm not a full time Java dev.
In production code, no matter the language, you have to guard against these issues. The nice thing about typed objects (immutable, anyway), is that once constructed and validated, they do not require tedious validation at every method and client they are passed into. That actually results in less boilerplate, and less test writing.
So just remember, Java always assumes your code is production-bound and requires upfront planning and checking. That's why it's unpleasant to prototype with.
That is about 4 lines, and a try finally statement around that and a try catch statement around that again and that is all that is required. 15 lines maybe, a bit more if you want to move it of the main thread (but then you are, IMHO no longer just doing a get request).
Lady Java - https://www.youtube.com/watch?v=Mk3qkQROb_k
The Streaming - http://jz11.java.no/trailer_video.html
Java 4-Ever - http://jz10.java.no/java-4-ever-trailer.html
There are some great talks from last year's conference there as well, many of them in English. The one by Tim Berglund about Git is great!
That was the best video for ANY computer topic I've ever seen. HILARIOUS.
I'm going to be smiling the rest of the day.
Object fields = Data Division; Bean = Copy Book
Practices that actively discourage functional style programming in favor of procedural style programming with widely scoped variables.
I'm glad Java is more widely used than C++, but it's a hard language to really love, despite being (mostly) how I earn my living.
Another example is the inability to just read a local file into a string with a simple API call. It's this way, again, because in production the program has to reason about paths and file systems and can't make assumptions about the validity (or size) of the file you just want to load and play with.
In production code, no matter the language, you have to guard against these issues. The nice thing about objects (immutable, anyway), is that once constructed and validated, they do not require tedious validation at every method and client they are passed into. That actually results in less boilerplate, and less test writing.
So just remember, Java always assumes your code is production-bound and requires upfront planning and checking. That's why it's unpleasant to prototype with.
I know the video is a joke, but that was harsh. I think there are quite a few good open source languages that only took about 5 years or so to get going with a decent runtime library. What takes years is to get adoption of new tools from the grass roots up, more so than implementation.
What if the button software was written in Java and did, in fact, nothing. Perhaps with a nice 40-something line stack trace in the log file. Meanwhile, a completely new worm targeting the Java platform crashed most of the infrastructure.
I took three years of it in community college and could probably pick it up again in a hurry. If I did pick it up again, I could probably easily double my current (very reasonable) salary.
But I'll be damned if I go back to writing COBOL, and I know a great many programmers like myself who feel the same way. And (modesty aside for a second here) they tend to be the better programmers.
So you're left with the "for-the-paycheque" programmers writing COBOL, and the programmers who couldn't find a job they cared about more.
That's who's going to be rewriting all those mission-critical COBOL apps once people want to start expanding into more modern languages.
Guy...
You don't just rewrite code that's been through the FDA approval process. It doesn't work like that. If you want to replace a java or c based medical imaging viewer... you can... but you have to prove it won't kill anybody. Or make any doctors cut off the left leg instead of the right one because a transform is flipped due to a DICOM attribute being interpreted incorrectly. Or that RTP calculations are still correct, and the PET/CT fusion is registering correctly. You can't get Radiation dosage calculations wrong by even a tiny amount.
And you have to prove all of that to the FDA and sign thousands of sheets of documentation swearing to it. And the standard fine print in that documentation makes you criminally liable if a bug is found.
And that's just 1 tiny industry that java is used in.
Multiply that difficulty by every life and death or money based industry and you begin to get an idea of what a monumental task getting rid of java would be. Think oil extraction and exploration systems... uh... no excuses there... they MUST work. Think industrial control systems... software for nuclear facilities, or even software for a simple coal based energy plant, can't just be thrown together. People in a city or area depend on these facilities to meet their energy needs.
Maybe I'm misunderstanding you... what is your definition of "wouldn't take long"?
I spent £100 on Geecon last year.