Hacker News new | comments | show | ask | jobs | submit login

> It has a very complicated type system, but doesn't even allow you to express things like function composition or generic sums at the language level.

Yeah, sure, it doesn't have Haskell's . or algebraic datatypes, just like lots of other languages. I'm not sure what's complicated about the Java type system, but I suspect you're trying to say you don't like subtyping. Moving on...

> Its syntax is stunningly verbose (e.g., no map literals; only now adding lambda literals; no type synonyms; no operator overloading).

No map literals, but Guava has leveraged generics in a brilliant way to reduce the duplicate declaration of types. Also, it brings a little bit of functional-style goodness with transform/filter. It turns even JDK 6 into something you can be productive with. I'm not sure the lack of type synonyms is really an issue, the type signatures are rarely complex enough that you need to obfuscate them. On the other hand, I'd give somebody else's right arm for an equivalent to newtype and deriving.

> There is no macro system or method_missing or any other way of really extending the language, except the ugly, unsafe reflection system ... so now all the libraries (Spring, Hibernate, etc.), use annotations and reflection to modify object behavior at runtime.

Not to forget proxy objects and interceptors. But yes, Java could definitely use a macro system.

But you're missing the elephant in the room, the existence of null. The bane of every Java programmer, dreading NPEs at each function call.

Checked Exceptions and null. I write a lot of Java and I generally enjoy the language, but those two things; if Oracle could fix those somehow I would be extremely pleased. I'm so tired of writing null checks and I hope Optional<T> is not the final answer since it too can be a null due to programmer error. I am really worried about Optional getting abused in Java 8...

Actually, I waffle on checked exceptions; it seems every 8 months or so I have a different opinion of them. Right now they suck.

> Checked exceptions

It always bothered me that Java forces me to explicitly handle/rethrow exceptions, yet happily overflows my ints without batting an eyelid.

I like checked exceptions in certain situations. I generally dislike the checked ones that come with Java ^1, but it can be very useful to create your own. I had a situation where I was interacting with an API that would sometimes fail over things I could not control. But, the code responsible for interfacing with the API didn't have the context for handling those errors.

Initially I threw a Runtime (ie: non compile time) exception. After going to production I realized that these were bubbling up to the UI and giving the user very intimidating error messages. So I changed the exception to a Compile time exception and then the compiler caught every place I let the error slip through to the UI. In my opinion it saved me a lot of time and made my code easier to maintain.

1: EG: Who is Java to say that this IOException should be handled in code? Perhaps this IOException should not happen and requires a developer's intervention.

InterruptedException is the worst. I know that I'm not calling Thread.interrupt() so why do I need to handle that case? It's already hard enough to reason about multithreaded code without worrying about future-proofing it for some case where it's somehow convenient for another developer to use Thread.interrupt().

I used to think checked exceptions were mostly an annoyance. Then one day, when working with a library doing I/O in Haskell, I realized that I couldn't pattern-match on whatever exceptions it may throw because I didn't know what they were and the type system was no help. Since then, I learned to live with the verbosity.

I'm the same way with checked exceptions. Right now I am in the pro-camp... but just give me a little bit. I am about to start a greenfield jvm project so I think I will be anti soon... but once its mature and I'm in bug fix mode I'll be heading back to the pro-camp.

  I'm so tired of writing null checks ...
Could the null checks of Java be compared to the ones used in C#? My C# code is often littered with ternary operators to deal with null values. And that's still not as safe as the Objective-C approach where one can just send messages to nil[0] which is really awesome imo.

I guess I kinda wonder if I could avoid the null checks in C# somehow ... I figured using a design by contract approach might be used to reduce the null checks somewhat.

[0]: http://stackoverflow.com/questions/156395/sending-a-message-...

> not as safe as the Objective-C approach where one can just send messages to nil[0] which is really awesome imo.

It's definitely NOT awesome. This stupid behaviour causes bugs all the time as you think you're doing an action but it's actually a no-op because something else failed and you get a nil. An NPE is awesome - it crashes immediately and you know exactly what is wrong. A silent no-op is the worst thing to debug.

Have you found C#'s null coalescing operator useful in these situations at all? I've found it to be quite handy.


> I guess I kinda wonder if I could avoid the null checks in C# somehow

Without seeing your code it is hard to say, but using an object extension might work?

On many occasions I've used object extensions to hide checking code deep inside the extension.

C# really got that feature right.

Can you show an example of how this can be done?

+1. If only they had introduced syntactic sugars for nulls, like user?getAddress()?getStreet(). It just doesn't have to be so painful. Writing functions used to be one of them and I'm happy it's solved.

FWIW, I have found using findbugs and @Nullable/@NotNull to be a blessing.

> Checked Exceptions and null.

Let's not forget about type-erasure generics. And the existence of arrays. And the fact that people still use arrays.

> Let's not forget about type-erasure generics.

Type-erasure generics are a fortuitous platform decision, because its what enables languages on the platform to have a good interop story while still having a more robust type system than Java does -- case in point, Scala, and why the .NET version died.

Exactly - checked exceptions is more of an policy / code style decision, null and NPE - roughly the same and in the end your spider sense just knows where to expect it.

But type-erasure of generics - that's the decision with which we will need to live till the end of time.

  - no multiple implementation of same generic interface
  - http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs
  - small performance hits..
  - casts
  - a lot more but I'm just sleepy right now.
I use and like Java, I love the backwards compatibility which it has - but there are times when crust can be removed with scalpel - and if not, you will need an jackhammer later.

I really don't have a problem with erased generics for the same reason a poster below pointed out.

> Checked Exceptions and null

I hear you.

> I'm not sure what's complicated about the Java type system

I assumed this was a reference to the unequal handling of "primitive" types vs Object types, with only the latter being able to be used with generics. Except for the weird "dual" object types which sortof are and sortof are not equivalents to the primitive types (int -> Integer, etc). WHich for methods needing to take actual primitives leads to having to do abominations of repetition such as http://www.docjar.com/docs/api/java/util/Arrays.html.

Some other bad things (not necessarily "complicated" but it does complicate the code that needs to be written):

Enumerations all have to have the same constructor params and therefore the same "shape". SOrt of defeats the purpose of enumerating ALTERNATIVES, I would say (this is part of the lack of sum types that was mentioned, though).

Others mentioned nullability. Some other things I didn't see others mention:

Botched covariance of arrays which leads to runtime failure where compile time should have been sufficient, http://k2java.blogspot.com/2011/07/parametrized-types-and-ar....

Some other minor things, such as botched clonability system, where Cloneable is a "marker" interface that doesn't tell you whether some type is really cloneable, a bizarre unforced error where a simple interface (you know, with an actual method called "clone") would have sufficed.

Optional<T> in jdk8 will help with null:


But without a pattern matching system, optional is still half of what it is in, say, Scala.

Why would you ever pattern match on Option[T] in Scala? In all my code, I do either:

    optionalF.map( _.whatever ).getOrElse(fallback)
or perhaps

    (optionalF <+> optionalFallback).getOrElse(finalFallback)
For those unfamiliar with <+>, see here: http://www.chrisstucchio.com/blog/2014/handle_failure_with_p...

I think that's a pretty extreme position. First, it will run a lot slower than the pattern matching alternative:

    optional match {
      case Some(x) => whatever(x)
      case None    => fallback
Second, I find the pattern matching code much clearer. Sure, it's also longer, but clarity trumps everything.

map / getOrElse is standard in all the Scala code I've seen. Abstracting over the monad is also a pretty big win that has made some big refactorings painless for me. Pattern matching is really only used by beginners IME.

Well, then maybe the beginners do it right? You have not argued against my two points: It's much slower, and less clear.

Clarity comes from familiarity. It's not an objective measure.

The speed benefit is well known, as is the corollary, premature optimisation. I expect checking for null is even faster if speed is the main concern. It's an engineering tradeoff, just like making the abstraction to a monad (or monad plus). In the normal course of events I'm more concerned about flexibility than performance and would prefer the abstraction.

Isn't map/flatMap and orElse enough, if only for Optional?

It's not enough for compactly expressing imperative logic. In Scala:

  resultOption match {
    case Some(x) => println(x)
    case None => println("error")
Without pattern match:

  if (resultOption.isDefined) {
  } else {
The .get is the problem.

There are several ways of doing it concisely with the Java Optional

  import java.util.Optional;

  public class Scratch {
      public static void main(String... args) {
          Optional<String> foo = Optional.of("foo");
          Optional<String> bar = Optional.empty();


          foo.map(Print::print).orElseGet(() -> Print.print("Error"));
          bar.map(Print::print).orElseGet(() -> Print.print("Error"));



      static class Print {
          public static <T> T print(T val) {
              return val;

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact