Java 8 Friday: The Dark Side of Java 8


At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem.

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.

The dark side of Java 8

So far, we’ve been showing the thrilling parts of this new major release. But there are also caveats. Lots of them. Things that

  • … are confusing
  • … are wrong
  • … are omitted (for now)
  • … are omitted (for long)

There are always two sides to Java major releases. On the bright side, we get lots of new functionality that most people would say was overdue. Other languages, platforms have had generics long before Java 5. Other languages, platforms have had lambdas long before Java 8. But now, we finally have these features. In the usual quirky Java-way.

Lambda expressions were introduced quite elegantly. The idea of being able to write every anonymous SAM instance as a lambda expression is very compelling from a backwards-compatiblity point of view. So what are the dark sides to Java 8?

Overloading gets even worse

Overloading, generics, and varargs aren’t friends. We’ve explained this in a previous article, and also in this Stack Overflow question. These might not be every day problems in your odd application, but they’re very important problems for API designers and maintainers.

With lambda expressions, things get “worse”. So you think you can provide some convenience API, overloading your existing run() method that accepts a Callable to also accept the new Supplier type:

static <T> T run(Callable<T> c) throws Exception {
    return c.call();
}

static <T> T run(Supplier<T> s) throws Exception {
    return s.get();
}

What looks like perfectly useful Java 7 code is a major pain in Java 8, now. Because you cannot just simply call these methods with a lambda argument:

public static void main(String[] args)
throws Exception {
    run(() -> null);
    //  ^^^^^^^^^^ ambiguous method call
}

Tough luck. You’ll have to resort to either of these “classic” solutions:

    run((Callable<Object>) (() -> null));
    run(new Callable<Object>() {
        @Override
        public Object call() throws Exception {
            return null;
        }
    });

So, while there’s always a workaround, these workarounds always “suck”. That’s quite a bummer, even if things don’t break from a backwards-compatibility perspective.

Not all keywords are supported on default methods

Default methods are a nice addition. Some may claim that Java finally has traits. Others clearly dissociate themselves from the term, e.g. Brian Goetz:

The key goal of adding default methods to Java was “interface evolution”, not “poor man’s traits.”

As found on the lambda-dev mailing list.

Fact is, default methods are quite a bit of an orthogonal and irregular feature to anything else in Java. Here are a couple of critiques:

They cannot be made final

Given that default methods can also be used as convenience methods in API:

public interface NoTrait {

    // Run the Runnable exactly once
    default final void run(Runnable r) {
        //  ^^^^^ modifier final not allowed
        run(r, 1);
    }

    // Run the Runnable "times" times
    default void run(Runnable r, int times) {
        for (int i = 0; i < times; i++)
            r.run();
    }
}

Unfortunately, the above is not possible, and so the first overloaded convenience method could be overridden in subtypes, even if that makes no sense to the API designer.

They cannot be made synchronized

Bummer! Would that have been difficult to implement in the language?

public interface NoTrait {
    default synchronized void noSynchronized() {
        //  ^^^^^^^^^^^^ modifier synchronized
        //  not allowed
        System.out.println("noSynchronized");
    }
}

Yes, synchronized is used rarely, just like final. But when you have that use-case, why not just allow it? What makes interface method bodies so special?

The default keyword

This is maybe the weirdest and most irregular of all features. The default keyword itself. Let’s compare interfaces and abstract classes:


// Interfaces are always abstract
public /* abstract */ interface NoTrait {

    // Abstract methods have no bodies
    // The abstract keyword is optional
    /* abstract */ void run1();

    // Concrete methods have bodies
    // The default keyword is mandatory
    default void run2() {}
}

// Classes can optionally be abstract
public abstract class NoInterface {

    // Abstract methods have no bodies
    // The abstract keyword is mandatory
    abstract void run1();

    // Concrete methods have bodies
    // The default keyword mustn't be used
    void run2() {}
}

If the language were re-designed from scratch, it would probably do without any of abstract or default keywords. Both are unnecessary. The mere fact that there is or is not a body is sufficient information for the compiler to assess whether a method is abstract. I.e, how things should be:

public interface NoTrait {
    void run1();
    void run2() {}
}

public abstract class NoInterface {
    void run1();
    void run2() {}
}

The above would be much leaner and more regular. It’s a pity that the usefulness of default was never really debated by the EG. Well, it was debated but the EG never wanted to accept this as an option. I’ve tried my luck, with this response:

I don’t think #3 is an option because interfaces with method bodies are unnatural to begin with. At least specifying the “default” keyword gives the reader some context why the language allows a method body. Personally, I wish interfaces would remain as pure contracts (without implementation), but I don’t know of a better option to evolve interfaces.

Again, this is a clear commitment by the EG not to commit to the vision of “traits” in Java. Default methods were a pure necessary means to implement 1-2 other features. They weren’t well-designed from the beginning.

Other modifiers

Luckily, the static modifier made it into the specs, late in the project. It is thus possible to specifiy static methods in interfaces now. For some reason, though, these methods do not need (nor allow!) the default keyword, which must’ve been a totally random decision by the EG, just like you apparently cannot define static final methods in interfaces.

While visibility modifiers were discussed on the lambda-dev mailing list, but were out of scope for this release. Maybe, we can get them in a future release.

Few default methods were actually implemented

Some methods would have sensible default implementations on interface – one might guess. Intuitively, the collections interfaces, like List or Set would have them on their equals() and hashCode() methods, because the contract for these methods is well-defined on the interfaces. It is also implemented in AbstractList, using listIterator(), which is a reasonable default implementation for most tailor-made lists.

It would’ve been great if these API were retrofitted to make implementing custom collections easier with Java 8. I could make all my business objects implement List for instance, without wasting the single base-class inheritance on AbstractList.

Probably, though, there has been a compelling reason related to backwards-compatibility that prevented the Java 8 team at Oracle from implementing these default methods. Whoever sends us the reason why this was omitted will get a free jOOQ sticker :-)

The wasn’t invented here – mentality

This, too, was criticised a couple of times on the lambda-dev EG mailing list. And while writing this blog series, I can only confirm that the new functional interfaces are very confusing to remember. They’re confusing for these reasons:

Some primitive types are more equal than others

The int, long, double primitive types are preferred compared to all the others, in that they have a functional interface in the java.util.function package, and in the whole Streams API. boolean is a second-class citizen, as it still made it into the package in the form of a BooleanSupplier or a Predicate, or worse: IntPredicate.

All the other primitive types don’t really exist in this area. I.e. there are no special types for byte, short, float, and char. While the argument of meeting deadlines is certainly a valid one, this quirky status-quo will make the language even harder to learn for newbies.

The types aren’t just called Function

Let’s be frank. All of these types are simply “functions”. No one really cares about the implicit difference between a Consumer, a Predicate, a UnaryOperator, etc.

In fact, when you’re looking for a type with a non-void return value and two arguments, what would you probably be calling it? Function2? Well, you were wrong. It is called a BiFunction.

Here’s a decision tree to know how the type you’re looking for is called:

  • Does your function return void? It’s called a Consumer
  • Does your function return boolean? It’s called a Predicate
  • Does your function return an int, long, double? It’s called XXToIntYY, XXToLongYY, XXToDoubleYY something
  • Does your function take no arguments? It’s called a Supplier
  • Does your function take a single int, long, double argument? It’s called an IntXX, LongXX, DoubleXX something
  • Does your function take two arguments? It’s called BiXX
  • Does your function take two arguments of the same type? It’s called BinaryOperator
  • Does your function return the same type as it takes as a single argument? It’s called UnaryOperator
  • Does your function take two arguments of which the first is a reference type and the second is a primitive type? It’s called ObjXXConsumer (only consumers exist with that configuration)
  • Else: It’s called Function

Good lord! We should certainly go over to Oracle Education to check if the price for Oracle Certified Java Programmer courses have drastically increased, recently… Thankfully, with Lambda expressions, we hardly ever have to remember all these types!

More on Java 8

Java 5 generics have brought a lot of great new features to the Java language. But there were also quite a few caveats related to type erasure. Java 8’s default methods, Streams API and lambda expressions will again bring a lot of great new features to the Java language and platform. But we’re sure that Stack Overflow will soon burst with questions by confused programmers that are getting lost in the Java 8 jungle.

Learning all the new features won’t be easy, but the new features (and caveats) are here to stay. If you’re a Java developer, you better start practicing now, when you get the chance. Because we have a long way to go.

Nonetheless, thigns are exciting, so stay tuned for more exciting Java 8 stuff published in this blog series.

Are you in for another critique about Java 8? Read “New Parallelism APIs in Java 8: Behind the Glitz and Glamour” by the guys over

Tags: , , , , , , , , , , , ,

22 responses to “Java 8 Friday: The Dark Side of Java 8”

  1. JB Nizet says :

    If default methods were allowed to be final, you could not implement 2 different interfaces containing the same method anymore as soon as one is final and you want to choose the other implementation. `synchronized` has to do with concurrent access to shared state, and interfaces don’t have any state, so I find it quite normal to let that as a choice to the implementation. And since default methods can’t be final, you can always override the default methods just to make them synchronized.

    • Maaartinus says :

      Good point concerning final, but this problem already exists as you can’t implement both Collection and something declaring long size().

      I disagree with synchronized as you can synchronize on class X {} which has no state either. You can also enclose the whole method body in synchronized (this) which is AFAIK equivalent. I’d guess this should work for interfaces as well.

      • lukaseder says :

        You can also enclose the whole method body in synchronized (this) which is AFAIK equivalent.

        Yes, except that putting synchronized into the signature formally makes it part of the method contract.

    • lukaseder says :

      If default methods were allowed to be final, you could not implement 2 different interfaces containing the same method anymore as soon as one is final and you want to choose the other implementation.

      I can see hwo this might have been a compelling enough argument for the expert group to leave final out of the game for interfaces. Yet, you may run into similar issues if 2 different interfaces contain the “same” method returning incompatible types, already since Java 1.0:

      interface I1 {
          int get();
      }
      
      interface I2 {
          long get();
      }
      

      You cannot implement both of the above interfaces. So in my opinion, the added value for API designers of having default final methods outweights the rare edge-cases, where people run into this kind of issue.

      Synchronized: It might be used as a contract for implementations, similar to Hashtable which made that a contract for subtypes like Properties. Agreed, this isn’t very contemporary and would probably be useful only in very remote edge-cases.

      • Maaartinus says :

        Actually, synchronized in the method signature is rather misleading as it doesn’t get inherited. You could consider it to be a combination of an implementation detail with a documentation, but a collection interface method documenting all implementations to be synchronized is pretty much useless (neither ConcurrentHashMap nor ImmutableMap would comply; actually no usable class would). You may require all implementations to be thread-safe in a sense, but there are too many useful options (and synchronized doesn’t belong to them).

        • lukaseder says :

          Actually, synchronized in the method signature is rather misleading as it doesn’t get inherited.

          Good point. I hadn’t thought of that.

  2. mwanji says :

    Why would you overload Callable with Supplier, since both have the same lamba signature?

    Also, I believe equals and hashcode are never added as default methods on interfaces because default methods always defer to methods on classes. As both are implemented on Object, the default would never be used.

    Not too sure about listIterator, but it would have required moving the method from AbstractList to List, which might have caused binary backwards incompatibility?

    • lukaseder says :

      Why would you overload Callable with Supplier, since both have the same lamba signature?

      Just because we now have lambdas doesn’t mean that lambdas is all we ever do. The two methods are very different methods. If you’ve written a ton of JDK 6 concurrent API with lots of Callables, and now you write a ton of JDK 8 streams API with lots of Suppliers, you *might* just think that you should overload this method for convenience. Of course, you shouldn’t, but this is not very obvious at first.

      As both are implemented on Object, the default would never be used.

      You’re right, and in fact, the compiler prevents this. You’re not allowed to have such default methods in interfaces. While this makes sense from a language perspective, it’s kind of weird because all sorts of contracts for equals() and hashCode() are already overwritten. Why not be able to provide a default implementation for such a contract? I guess this shows that the whole notion of methods from java.lang.Object is a bit quirky (e.g. the wait() methods)

      Not too sure about listIterator, but it would have required moving the method from AbstractList to List, which might have caused binary backwards incompatibility?

      Interesting thought. Could you show an example?

      • Ian Robertson says :

        Another problem with adding default implementations of methods which already existed on the interface previously is that it could break backwards compatibility. For example, there might have been code that depended on MyList’s equals method being the default implementation from Object. While such code is a clear violation of spec, Oracle goes out of their way to avoid breaking code that already “works”.

        As for listIterator – that’s a harder one to figure out – hard to imagine code depending on a NoSuchMethodError. I don’t think binary compatibilty would be an issue, since it appears that default methods are invoked with invokeinterface when called against the interface, and invokevirtual when called against the class, just like other methods.

        • lukaseder says :

          For example, there might have been code that depended on MyList’s equals method being the default implementation from Object

          While that is a remote possibility, I doubt that this is a motivation for the expert group to have decided upon things the way they are. Other comments on this post have indicated that no matter what default method matches Object.equals(), the actual Object.equals() implementation will always take precedence. It is simply not possible to override a method from the concrete class hierarchy from an interface. Hence, it simply doesn’t make sense to implement a default List.equals() method.

          As for listIterator – that’s a harder one to figure out – hard to imagine code depending on a NoSuchMethodError

          Yes, that’s a better example than equals()

  3. Maaartinus says :

    Oracle not adding the default methods to List should be no problem as you could do it in YourList extends List. Unfortunately, many of them use modCount, which you can’t have in an interface.

    You could provide it in the implementations (it’s just one field) and use abstract accessors in YourList, but they would have to be public. Too bad they din’t fix the visibility problem.

    • lukaseder says :

      Oracle not adding the default methods to List should be no problem as you could do it in YourList extends List.

      Yes, you could, of course.

      Unfortunately, many of them use modCount, which you can’t have in an interface.

      Which leads to the other feature omission that you’ve mentioned, too: visibility modifiers on default methods (or on any interface methods). That, too was discussed on the lambda list, and omitted because of the time constraints, and some uncertainty about how to handle package visibility on interface methods (e.g. by introducing a package keyword).

      • Maaartinus says :

        Agreed. Interfaces forcing all methods to be public was a terrible idea and it’s pity it wasn’t fixed now. Given that package is already a keyword and that it can’t appear anywhere but in the package declaration, it’d be no problem to recycle it for access control, although recycling default feels better (which is not an option anymore; I agree with your arguments).

  4. Edwin Dalorzo says :

    What could be the purpose of a synchronized method on a interface? After all the interface does not have state, so what would you be synchronizing access to?

    Also, final interface methods? Does that make sense? After all the whole idea of an interface is to create implementations of it. You are saying that you want to seal an interface method because it has a default implementation?

    • lukaseder says :

      What could be the purpose of a synchronized method on a interface?

      One reason is to establish a contract already on the interface level (why not…). Another is: Because you can. I mean, you can write this:

      interface I {
          default void sneakySynchronized() {
              // Unexpected!
              synchronized(this) {
              }
          }
      }
      

      And because this can be done, why not just allow promotion to the API even if it’s useful only in remote edge-cases?

      that you want to seal an interface method because it has a default implementation?

      Yes, because that’s the whole point of final. Imagine overloaded convenience methods:

      interface I {
      
          // No point in overriding this
          final default void run() {
              run(null, null);
          }
      
          // No point in overriding this
          final default void run(SomeType x) {
              run(x, null);
          }
      
          void run(SomeType x, SomeOtherType y);
      }
      
      • Edwin Dalorzo says :

        Let me play the Devil’s advocate here, just for the purpose of making the conversation interesting.

        In the case of the synchronized suggestion, this could be something that could have been argued of the traditional interfaces prior to the JDK 8 as well, but we know that keywords like synchronized, final and strictfp should go in concrete implementations and not interfaces because otherwise it would force every implementor to pay a price they may not be interested in. That question would be what if I want an implementation of such interface not to be synchronized. Also, is hard to reason about synchronization in an interface, I mean, you don’t even know what the state of the implementor would be, how do you you synchronizing this would prevent a race condition.

        They actually David Holmes and Joseph Darcy discussed some of this in the Lambda Mailing List (http://mail.openjdk.java.net/pipermail/lambda-dev/2012-October/006084.html)

        On the other hand, final methods on an interface are kind of counterintuitive for me since the hole purpose of the interface is to be implemented. I think such weird cases should probably fit better in an abstract class, but if that were the case, then we should not only support final, should support other modifiers like protected and private as well. Ultimately, an interface would be just like a class, but without state. I guess with interface now supporting implementations, it becomes a gray area how far interfaces should go. Time will tell, I guess, and this is what the expert group is expecting. You can see Brian Goetz said:

        “[…] we expect that this will inevitably create pressure for
        additional features in interfaces, like, say, private methods. Some of
        these we may try and anticipate by including in this round; others we
        may wait for usage patterns to emerge […]”

        http://mail.openjdk.java.net/pipermail/lambda-dev/2011-December/004361.html

        • lukaseder says :

          Thanks for digging up those links. I wasn’t aware of the first one where synchronized and strictfp were discussed. One can also see the importance that was given to this discussion :-) Myself, I wouldn’t want to argue about synchronized (or strictfp, which I had forgotten in the blog post) too long. In the end of the day, the added value for any API designer is marginal, as opposed to final and visibility modifiers, which would be an essential further evolution of interfaces being classes without state – which they should be, in my opinion, and which is again an argument against the default keyword.

          Another argument in favour of final in interfaces is the possibility to prevent shadowing of static methods that may now be declared in interfaces. This type of shadowing is hardly every useful, and in my opinion, a complete anti-pattern – i.e. by default, all static methods should be written as static final, also in interfaces. But that obviously won’t happen in the JDK

  5. Smyatkin Maxim says :

    Good article, made my brain work for a while :)
    But, even thought I started learning Java after working with C++ for 6-7 years just 4 months ago, I think I’m able to give several details/corrections here.

    First, I didn’t think of ambiguous method call with lambdas and thanks for pointing it out, but what’s wrong with call((Functor) (Lambda_expression)) syntax? For me it looks perfectly clean, in spite of being a little bit bigger. It’s perfectly readable and developer’s intention is obvios. Isn’t the readability the only thing which matters? :)

    Second, about synchronized. As Maaartinus pointed out, it isn’t part of method’s contract, because it is not being inherited. So, it has no use in interfaces. Moreover, as Interface has no state (it can’t have instance or even static fields) there is no point in synching on instance or static methods. So, the only case in which you may want to synchronize something in interface’s methods is when you use some shared objects: IO streams, Collections, etc. Hence, you need to synchronize on these particular objects using synchronize block. It will also make your intention easy to understand for other developers.
    So, I don’t think that you will ever need to make up the lack of synchronized methods with synchronized(this) or synchronized(I.class). I believe my point on this particular topic is very solid.

    Now, about static methods in interfaces. Nothing random about it – actually, it’s even more intuitive than synchronized keyword. The tricky thing about static methods is that they are completely out of inheritance tree, which makes both default and final keywords completely useles.
    If you write something like this:

    interface I1 { static void out(System.out.println("I1")); }
    interface I2 extends I1 {  }
    

    And then try to execute I2.out() – the compiler won’t find out() method in I2. If you try to “override” it – you just create completely new static method. So, here is no point in making them final/default.

    Now, default Object’s methods in interfaces… You’re correct that wherever conflict between Class’ and Interface’s default methods arrises – the former will be prefered. The reason is backward compatibility and is very old one. When Java was being designed it was supposed to be much easier to learn than C++, right? One of the toughest concepts was multiple inheritance.
    E.g., in C++:

    class A {
        public:
            void doX() { .... }
    };
    
    class B {
        public:
            void doX() { .... }
    };
    
    class AB : public A, B {
        // which doX() I should inherit? :(
    };
    

    To get rid of it, interfaces were sepparated from classes in Java. Now, if interface Parent_I declared method X and class Parent_C implements the same method, then class “Child_C extends Parent_C implements Parent_I” will use Parent_C’s implementation. So, the same happens in Java 8 with default methods. You already pointed out that Java 8 is too complicated in some places (and I absolutely agree), but letting you implement Object’s methods in interfaces probably would make things even worse and could move us closer to C++’s multiple inheritance.

    One more interesting point about final + default. Well, first of all “default” means that it is the most general method’s implementation, API designer could think of. But you’re encouraged to override it with what is supposed to be the most effective for your use case. So, it probably shouldn’t be final.
    But what is more important, is the conflict between two interfaces if finals were allowed. Let’s look at the example:

    interface I1 {
        default void doX() {...}
    }
    
    interface I2 {
        default void doX() {...}
    }
    
    class C1 implements I1, I2 {
        // which doX() implementation I sould use? :(
    }
    

    The compiler won’t let it pass – it can’t figure out what to do in C1.doX()! But no problem, you can fix it:

    class C1 implements I1, I2 {
        public void doX() {...}
    }
    

    But wait! What if we actually could make default methods final? We would not be able to override them in C1. So, how could we use I1 and I2 interfaces together than?

    And finally, even though I agree that Java 8 asks you to remember a lot of stuff you would prefer not to keep in your head, I don’t think that Predicate, Consumer, Supplier, etc.. are so complicated. I think the naming is pretty obvious and even if you have to use them – there is no problem to figure out which one is your choise. In my opinion, it is one of the cleanest parts of Java 8 :)

    But, as you said, Java is more and more complicated to learn. I have the same impression and it’s quite fresh because I started with Java 6 at my previous job few months ago, then prepared for OCJPs for Java 7, and after that OCJP 8 became available – so I started to learn Java 8. And yes, even though some good stuff has been added, the language became much more complicated.
    Usually, one expects new features to be somehow intuitive to learn, but in Java all the releases seem to bring a lot of exceptions and underdone stuff with them. E.g., Generics without support for primitive types (since Java 5) lead to lots of specialized classes in Java 8 (IntPredicate, mapToLong, IntStream, etc…); this new “effectively final” stuff; default + Object’s methods; just to give a few.

    • Maaartinus says :

      Agreed. Java 8 makes it also easy to write a simple stuff in an unreadable way by overusing the functional stuff (or misusing it for side effects). Sadly, the specialized classes (IntPredicate, QuaternaryDoubleObjectIntLongCharFunction, …) will stay long after the primitive generics problem gets solved.

      I guess, they should’ve deprecated a few things, like synchronized, wait, notify, notifyAll, (and also Date, StringBuffer, Vector at al.) and threaten to remove them soon. Why? There’s no reason to synchronize on an arbitrary object and it’s no good idea either. See also https://projectlombok.org/features/Synchronized.html.
      Moreover, wait at all pollute the namespace.

    • lukaseder says :

      Thanks for taking the time to comment.

      but what’s wrong with call((Functor) (Lambda_expression)) syntax?

      Remember generics? Before generics, we used to cast every type and their dog to some subtype, e.g. when unwrapping them from collections. I dare say no one working with Java on a daily basis misses explicit casting. If you really want to argue about what’s wrong with the call((Functor) (Lambda_expression)) syntax, I challenge you to tell me what’s wrong with the following syntax:

      call(new Lambda_expression_named_type() {
          @Override
          public String something(int something) {
              // ...
          }
      });
      

      Clean, a bit bigger, perfectly readable, intention is obvious… ;-)

      Second, about synchronized

      Clearly synchronized on default methods would raise more questions than it would solve problems. The argument here was a bit academic, showing that we are inheriting a lot of legacy in a highly irregular language that causes lots of confusion when new features are added, differently.

      In my opinion, it wouldn’t be unwise to forbid synchronized on class methods (emitting a warning for now, removing support in Java 11 or so), just to be consistent.

      The tricky thing about static methods is that they are completely out of inheritance tree, which makes both default and final keywords completely useless

      They are indeed “out of the inheritance tree”, which was a conscious decision at some point in their design, yet again a very irregular decision. Perhaps it only means that allowing for static methods to be linked from subclasses was always a mistake:

      class C1 { static void out() { System.out.println("C1"); } }
      class C2 extends C1 { }
      
      // Perfectly fine
      C2.out();
      

      Again, the criticism here is the fact that all of this legacy is still lying around when the language evolves into a very inconsistent new direction. Perhaps it should be forbidden to reference C1.out() via C2.out() to make the language more regular.

      When Java was being designed it was supposed to be much easier to learn than C++, right? One of the toughest concepts was multiple inheritance.

      I do understand this rationale and I do understand that many of the early Java language designers must have been very terrified by their earlier C++ experience. But it was a time when few people recognised composition over inheritance. Today, our fear has diminished and we can handle multiple inheritance (of behaviour) much more easily in our abstractions, mostly by avoiding it entirely.

      The Scala collection libraries, on the other hand, are a good example where this has been applied in a good way. The example you’re showing is a corner case and it happens also with “ordinary” Java:

      interface I1 { void x(); }
      interface I2 { int x(); }
      
      // Oops, conflicting signatures for x()
      class C implements I1, I2 {}
      

      This is nothing new, but it’s OK. A small price to pay. You simply can’t implement both I1 and I2 at the same time. Big deal…

      but letting you implement Object’s methods in interfaces probably would make things even worse and could move us closer to C++’s multiple inheritance.

      You’re arguing your point from the perspective of the fact that Object already has those methods. I’m arguing from the point of view that Object should have never had those methods in the first place, instead of making quirky rules about some “special” methods that can never have any default, even if that would have been terribly useful for toString(), hashCode(), and equals() (which should be default methods on new types ToStringable, Hashable, and Equalable).

      Well, first of all “default” means that it is the most general method’s implementation, API designer could think of. But you’re encouraged to override it with what is supposed to be the most effective for your use case. So, it probably shouldn’t be final.

      Apply your argument to class methods and it would work just the same. In fact, if any such argument is valid, then only either both class and interface methods should be allowed to be final, or none. All other solutions are inconsistent and quirky, requiring much more complex explanations about what each different type of object really is and what it isn’t. This is fine in every day work, but it complicates the language unnecessarily.

      We would not be able to override them in C1. So, how could we use I1 and I2 interfaces together than?

      I understand this argument, but as an API designer, I know my tools (the language), and I won’t make this mistake. Besides, see again my own example:

      interface I1 { void x(); }
      interface I2 { int x(); }
      
      // Oops, conflicting signatures for x()
      class C implements I1, I2 {}
      

      The argument is bogus. Language users can shoot themselves in the foot. It’s not the job of a language designer to educate their users about design principles.

      I don’t think that Predicate, Consumer, Supplier, etc.. are so complicated

      So you deny there is some cognitive friction in remembering all of the following, for example:

      public interface ToIntFunction<T> {
          int applyAsInt(T value);
      }
      public interface Predicate<T> {
          boolean test(T t);
      }
      

      I mean, why is “ToIntFunction” a concept that is still “sufficiently Function-ish”, but “Predicate” is not, such that neither the term “Function” is repeated, nor “apply”, nor does “Predicate” make any reference to the fact that a primitive boolean is returned? I would have expected:

      public interface ToBooleanFunction<T> {
          boolean applyAsBoolean(T value);
      }
      

      Well, we’ll all live with this now. But as an API designer, I usually take a moment to find the right one. From the type name itself, I simply don’t know for sure what the best choice is.

  6. Smyatkin Maxim says :

    Well, I have never said that the changes are not confusing. I meant they can be logicaly deduced given some OOP and syntax knowledge.

    Old anonymous class syntax was too noisy and it obfuscates the desire to implement one single function behind creation of a new class. Of course, it seems clean for you as you’re used to it after years of Java experience. The moment when you see the construct, you unconciously look for the overriden method. But no, it’s not clean enough. As I mentioned, I have very small Java experience and for me the new lambda syntax looks much better.
    Moreover, somewhere in heart I still hope that one day lambda functions internally will be REALLY implemented like functions. Not like class methods. Just for sake of optimization.
    I was probably somewhere in high school when Java 1.5 has been released :) , but as far as I understand, the main problem was “unsafe type casting” rather than some additional typing. So, you could place a Cat into a Dog cage. It’s not the same with lambdas. You just help the compiler a little bit here.

    I totaly agree that statics are confusing. Static methods in classes were designed really bad. Static methods in interfaces fixed the mistake, but I don’t sure if it worth it. So, now:
    – with classes you can call superclass’ method via subclass. And also, you can call static method via object reference: instance.static_method(). This one I particularly hate, as it breaks readability a lot.
    – with interfaces both problems were solved, but inconsistency was introduced.

    No, my argument with finals won’t work the same with classes :) But this one is my personal opinion and may be wrong.
    Class is what an object primary is. And sometimes you know how the whole kind of such objects should behave. E.g., all the vehicles will rotate wheels on moving , etc. So, you may wish to make it final.
    But interfaces, on the other hand, represent some secondary behaviors common among separate class hierarchies. And any class in particular will have to implement it on its own, because only its developer knows how to do it. Here is the lack of finals.

    About Predicate’s, Function’s, etc methods… you have no idea how angry I was with methods’ naming in Java. After moving from C++ collections to Java collections I couldn’t understand why you add() ellements into Sets and Lists, BUT you put() them into maps. Then, I met concurrent collections and especially BlockingQueue interface – I was extremely dissapointed to explore all the add, put, offer, take, remove, pull, element, etc.. methods. And all of them with different behaviours for different subclasses. And finally, I had to remember these java.util.functions.* SAMs.
    I have no idea why the language, wich was positioned as crossplatform and EASIER C++ clone, became so inconsistedly designed :(

    • lukaseder says :

      Of course, it seems clean for you as you’re used to it after years of Java experience

      I never said that. I have always dreaded anonymous classes. I just opposed your argument in the case of what appears to be an unnecessary lambda cast to me.

      Moreover, somewhere in heart I still hope that one day lambda functions internally will be REALLY implemented like functions

      You could use Scala in the meantime. I doubt that Java will be really state-of-the-art functional any time soon… Scala on the other hand has built-in support for real “anonymous” functions and structural types. Here’s how you can implement anonymous structural types with Java 8: http://benjiweber.co.uk/blog/2015/08/07/anonymous-types-in-java. Horror!

      But this one is my personal opinion and may be wrong.

      Welcome to this equally opinionated (and possibly wrong) blog, then! :-)

      But interfaces, on the other hand, represent some secondary behaviors common among separate class hierarchies. And any class in particular will have to implement it on its own, because only its developer knows how to do it. Here is the lack of finals.

      I understand this argument, but it is biased by a presumed preference for a certain style of programming. There is absolutely nothing wrong with the following style (which I would have liked):

      interface I {
          final void foo(String s) {
              foo(new StringReader(s));
          }
          void foo(Reader r);
      }
      

      The point being that there is absolutely no reason to override the convenience method foo(String). Only foo(Reader) should ever be implemented. People have done this with abstract classes all the time. Other languages do this with extension methods. It’s a very useful technique, and I don’t see why anyone would educate me not to proceed with this technique. Or to put it in your words: Sometimes, I know how the whole kind of such convenience methods should behave. E.g. all String convenience methods that correspond to equivalent Reader methods will wrap the String in a StringReader. I really don’t see how your argument applies to vehicles and wheels but not to the above.

      So, this is why I argue that either final should be possible everywhere or nowhere, for consistency.

      you have no idea how angry I was with methods’ naming in Java

      I can imagine :-) The main reason for Java’s success is the fact that it is the default language on top of the JVM and the JVM totally rocks… The libraries are really mediocre, at best. See also this rant about the lack of functionality in the Stream API

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 2,970 other followers

%d bloggers like this: