This is irrelevant, but I just had my first Java experience yesterday. I decided to make an Android app, it's a simple remote control for hobby projects (Raspberry Pi/etc) that gives you a customizable UI with buttons that perform API calls. It'd be pretty simple to do in Python, which is the language I am proficient in, but Android development sounds fun so I wanted to get started in that.
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?
The standard method for getting around this in the java world is to create 'sane' libraries (in the case of android http, something like http://loopj.com/android-async-http/ ) and then just use the library. The strong point of Java is just how many libraries there are - you often don't need to touch the actual built in Java APIs. On the server side (non-android) this has gone even further through the use of new annotation based libraries that generally autodiscover classes in your code, removing a lot of boilerplate.
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.
I feel something as commonplace as making a HTTP GET or POST should be standardized at this point. There are many implementations which still require a decent amount of boilerplate.
The core java library has mostly been kept unchanged for a decade now (I think?). Java 8 is doing the largest change to it in a long time by changing the collections to work with lambdas.
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.
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.
Yeah, this is the argument I got tired of and why I jumped ship out of developing in Java for over a decade: "Java is pretty painful, but don't worry.. the tools are so good you don't have to write much code."
This blows my mind and this may be the reason I have so many problems dealing with frameworks of any kind.
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.
No, you're normal. Couple things though: The knowing how to fix it when it breaks isn't a real problem in my experience with Java... the stuff that tools tend to generate for you is so boilerplate there's not much mystery there, so I never felt abstracted from the meat of the programs, really.
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.
Thanks for clarifying checked vs unchecked. Your comment makes perfect sense, and is what I expected. The thing is that I really don't like handling an exception higher than where it makes semantic sense (which is the actual call, in this case), so propagating higher is even less clean, for this specific example. I guess the problem here is the checked exception, but I can see the rationale, if it's impossible to optionally handle an unchecked exception.
What you could do is initialize the Url in a static block or in a `{{ }}` block and handle the exception there. You would still have the exception handling, but not in your logic, and the static block would ensure that if the Url is indeed malformed, the exception will throw as soon as the class is loaded in memory, instead of only when the Url is instantiated during in logic.
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.
Putting it in a static block is bad advice, there are many other places you could put initialization.
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).
The idea is that if an exception has to be thrown, it will throw as early as possible, making it pretty obvious during development that there's a bug - whenever you try to run your app it will exit with an error about the MalformedUrl. Notice I said he/she should catch the exception in the static block and log it, pretty hard to have an issue tracking where the error comes from.
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.
Thanks, that's a better solution. I still dislike having to handle something that'll never happen, but this is probably the best one can do in this case.
regexp.MustCompile doesn't result in a compile error; it panics at runtime. The purpose is to simplify compiling regexps at the global level, and the assumption is it's a hard-coded regexp that can't be wrong.
What you described is mostly a problem of the standard library. Sometimes it's too low-level and it's better to use 3rd pary libraries for things like HTTP. There should definitely a way to create urls without having to check exception (ie. it would throw a RuntimeException that doesn't need to be checked).
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...
Scala is such a language, at least to me it was a pain to go back to Python after learning Scala. And Scala's tooling is IMHO better than Python's these days, although still not as smooth as Java's.
Me too, mainly 1, which makes you feel the language is slow. Contrast with Go (a much simpler language, but feels modern because it compiles so cleanly).
Hmm, yeah, I see what you mean. I think Go does a pretty good job of being a middle ground between Java and Python, it has the static typing with all the tooling advantages it brings, without the annoying need to be explicit about everything. I think you'll like it.
I quite like Go but I see it mostly as a C++ replacement, not Java or Python replacement. Java has far better tooling. It also has quite weak type system (no gnerics).
Dart is closest to my ideal of a general purpose language.
Go tooling will likely get better as time allows IDEs / plugins to be written.
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.
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).
It's a batteries-included, statically-typed language that's safer and harder to get wrong than C/C++, but faster than any dynamic language, and it has a fast, high-quality JIT compiler with well-written warnings and errors. Overall it's a very safe choice.
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.
> 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, ...
I don't know much about the other languages you mentioned, but C# is multi-paradigm, allowing certain functional and declarative programming practices that Java does not. It's much more expressive than Java.
Yeah, I don't use C# and I don't know if you can do that, I think you have to create a class and declare them as methods like Java.
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.
Still it makes it fundamentally an OO language, because C# closures and lambdas are mapped to System.Delegate class.
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.
Well, Ruby is OO too, even moreso than Java because there are no primitives in Ruby. Even the "integers" in Ruby are not ints but objects with lots of methods. But Ruby has a lot of powerful, functional, expressive, declarative, and meta language features that Java lacks. Java is like an OOP straightjacket, that's why it gets so much hate.
Type inference = the compiler being able to figure out what types your variables, function arguments, and return values are without you having to explicitly tell it.
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.
What you are describing (calling it overloading,) having different functions of the same name implement different algorithms based on types, is called "ad-hoc" polymorphism, but Java isn't limited to that, and it isn't the best way to do things, because you invariably end up duplicating parts of the algorithm.
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.
Forgive me for only considering parametric polymorphism as the real thing :)
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.
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.
Huh? You don't typically have an array of primitives? Says who?
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.
What's sad about Java is that the whole type-safety, compile-time-checks, minimal-runtime-surprises etc has gone out of the window since the DI craze, so now we have a platform that is as "unsafe" as Python et al, whilst still being relatively heavy to set up etc.. I guess there's still the advantage of having extensive libraries..
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.
Yeah, it's not a big risk to production, but it just makes the development loop that much more frustrating. There's nothing more annoying than building, deploying, starting, before discovering that you mispelt a bean name...
Ah! I see where my confusion lies. As others have commented, it sounds like you're talking about using a tool that does dependency injection for you; perhaps something configured in XML. I see how a tool like that would subvert a compiler's type checker.
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.
I don't think this can be called DI, because you're not injecting them, you're passing them to the constructor. The problem with this model is that you're polluting your constructors with non-functional arguments (for example the logging provider), and that if you want to change one of these, you have to change all your constructor calls!
> 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.
> I don't think I've ever injected a non-functional dependency
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 :(
I think the last two reasons are the most important ones, as the first to are relatively common.
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...
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.
No, that will force whoever is calling your method to handle that exception, now without knowing the actual context ("why does getWeatherForecast() throw MalformedURLException?" etc.). It also leaks an implementation detail, i.e. the use of URL-whatever.
A RuntimeException, and any subclass of it, is unchecked and so the compiler does not force you to handle it.
Oh, right, you convert it to RuntimeException (ostensibly in multiple places where you don't want to handle the exception) and catch that one as a general exception. It makes sense now and sounds like a good solution, thank you.
Well, in more forgiving languages, such as python, you are just implicitly being "hacky" as you put it. E.g. errors - if they ever occur - will happen in runtime. Java tries to move this to compile time, making them visible. It's a tradeoff really.
No, in Python you never silentrly ignore all exceptions (well, you can, but it's a big no-no). You can just opt to not handle them, but they'll crash your program. It's a tradeoff, as I know which exceptions are likely to become a problem and which aren't, and I can choose to handle the relevant ones and let it crash in the rare case that an exception I didn't think likely occurs.
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).
> 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.
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.)
The reason is that Java is designed for enterprise, where "I know which exceptions are likely to become a problem and which aren't" cannot be trusted. With giant code bases and mediocre developers you kind of need to have checked exceptions or you'll have exceptions popping up in the most unexpected places.
Unfortunately, mediocre developers are perfectly capable of learning to either catch and silently ignore checked exceptions (checked exceptions actually promote this habit, which can be successfully used in other languages with exceptions) or rethrow them as runtime exceptions.
The good thing is - newer java libraries use unchecked exceptions in the vast majority of their APIs.
That's not a good thing when you are trying to write absolutely bullet-proof code that will not fall over and can recover / retry robustly.
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.
When I come across exceptions that get silently "eaten" I want to throw the dev out of a window. I've lost days of my life tracking down inscrutable bugs only to find something like this: catch ( Exception e ) { /eat it/ }
Ah, that clarifies it a bit. Python takes a more "we're all grownups here" approach, which I find suits me better. Go takes a similar approach, but I can see how Java meets a different need.
Unfortunately, all the really enterprisey Java stuff completely kills that: now that everything is injected you lose all the compile-time protection that is the point of using Java!
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.
I've used Java for a few years on Android differences for Scala past proguard are minimal for building and performance is the same as it's just compiled Java bytecode and then to dalvik
Oh, sorry, I just recalled that it was Clojure that I had heard about, so strike my above comment. I'm afraid I'm not familiar with Android or Java anywhere near enough to use Scala, as the learning curve would be much higher then.
I will definitely consider it when I know a bit more about it, though, as it seems a much saner language.
Yep, even Clojure does, but have never tried it really for Android, but it's just as possible as Scala. Jython has been possible in the past, but I haven't seen any recent projects updated to use it in the past 1-2 years.
Java is old enough that the current best practices recommend using the language and libraries differently than originally intended.
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.
I'm really, really sorry that most of today's developers (it seems) consider checked exceptions to be a bad practice. Sometimes I feel like I'm the only person in the world still advocating their usage. I'm currently working on an application that uses libraries developed by people who had the same opinion about checked exceptions as you. Of course, their code still throws exceptions. Except now, I don't know whether a method I call can throw an exception and whether I should write code to handle it until it all blows up at runtime (more often than not - in production). So what do I do then? I write a try-catch to handle their exception. The exact same thing I would have done had they decided to throw a checked exception. Only now, I don't get any warning that I should handle possible exceptional behavior at compile time, I get it at some point during runtime.
I'm really, really sorry that most of today's developers (it seems) consider checked exceptions to be a bad practice. Sometimes I feel like I'm the only person in the world still advocating their usage.
You're not alone. I find checked exceptions to be an invaluable language feature.
Which API were you using? Java's HttpUrlConnection is notoriously badly designed [0] On Android (and elsewhere of course) you can use Apache's HttpClient instead which is a bit less horrible [1]. Part of this problem is checked vs unchecked exceptions, and some of the older APIs in Java use checked exceptions too heavily, IMHO. On top of that, I think all the Java IO stuff provides a pretty leaky abstraction over the system calls.
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.
I started using HttpClient, but I saw a blog post that said that HttpURLConnection should be used by all new apps, so I went with that. I can't tell you about checked vs unchecked exceptions, as I just found out about them a minute ago.
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!
The Android team has dropped support for Apache HttpClient. The party line is that if you are targeting Gingerbread or newer (and you should be), then use HttpURLConnection.
> I'd be curious what other people think about this.
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.
You don't need to leak abstractions on exceptions. Libraries should wrap the exceptions and provide meaningful exceptions, not dependent on the exact implementation. Example:
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.
Java is just not made for quick prototyping. In production, the URL will come from somewhere, not written by hand, so it would need to be checked anyway. Sadly there's no quick-prototyping mode in Java where you can say, just call this URL string an don't bother checking.
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 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.
> 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?)
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.
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.
Checked exceptions seem like a great idea, but I can see how the execution of them is irritating. If I were to do Android I would go directly to Scala, which looks pretty awesome.
It does, but when you have to learn Java and Android development in one go, learning Scala on top of that (since all the docs are in Java), is pretty daunting.
Did you pull in a library to do the HTTP work or just use the raw HttpURLConnection?
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 used this in the end: http://loopj.com/android-async-http/, it exposes an interface that does not make me shudder, and I am guessing that is a very good thing.
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...
A checked exception is one that the compiler forces you to write a check for. An unchecked exception ("the good kind") is one that you could write a check for if you want to.
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.
Alas, as much as I like golang, this is one area that it seems to have got worse than Java: error codes instead of exceptions, preferably unchecked exceptions like C# uses (and other languages, I presume)
AndroidHttpClient has a executor method, which take your HttpGet object and a transformator and the default one will give you a string, which is the body of the request.
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).
It looks like it, I just wish that the most frequently used libraries were standard in the language. That said, it's really magical for me to write something that runs natively on my phone, so it's still lots of fun.
Java is just not made for quick prototyping. In production, the URL will come from somewhere, not written by hand, so it would need to be checked anyway. Sadly there's no quick-prototyping mode in Java where you can say, just call this URL string an don't bother checking.
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.
Yay Oracle! Save us from the 42 year lag to create an open source alternative.
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.
The sad irony is that it is the termination of COBOL that could actually bring society to its knees. If Java died, it wouldn't take long for all that code to be rewritten in Go. But how many COBOL programmers do you know?
"...If Java died, it wouldn't take long for all that code to be rewritten in Go..."
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 made the assumption that we would still have access to the original source code. So it wouldn't be so hard to translate Java to another modern language, e.g. one of the most modern being Go. On the other hand, so few people know COBOL these days that even with source code, translating those systems would take considerably longer.
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.
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?