DEV Community

Discussion on: Modernizing Java - A language feature wish list (Part 1)

Collapse
 
wimtibackx profile image
Wim Tibackx

Thinking from the POV of starting out in an existing codebase that uses these features, I’m not sure I agree with all your points.

Extension methods: I absolutely agree that it’s an inconvenience to always have to add libraries like Apache Commons to projects, and having to divert to those for a large part of the use cases. What seems to me like the disadvantage of extension methods though, is that it would be less explicitly clear what is actually happening and more difficult to port to other codebases or even parts of the same codebase.

If now, I remember a call to StringUtils.leftPad, I can check which classes named StringUtils are on the classPath, or if that fails google which classes called StringUtils offer leftpad functionality and go from there. In the extension methods scenario, I wouldn’t really explicitly know where the leftPad method came from (if I only remembered that line), and especially junior developers might think it’s just part of the standard library, not realizing extension methods were being used. Maybe this can be worked around with good IDE support, but I’m not sure.

Strict null-ness: How does this work with implicit nulls? For example: I have an object called Person, which I retrieve from database (for example via Hibernate). Person has firstName, lastName, and age. For one person I might retrieve, the age could be null. What if I then pass in person.age to some method which accepts an Integer argument? I would assume that Ceylon can’t guard against this case at compile-time?

Additionally, there’s also the difficulty of backwards compatability. Given this syntax, this seems to be the one feature on your list that breaks that.

Compiler-checked reflection: That is a very nice feature!

All in all, nice overview, hadn’t heard of Ceylon or Xtend yet.

Collapse
 
martinhaeusler profile image
Martin Häusler • Edited

Thanks for the response! I agree that some of the features I described in this blog post (in particular strict null-ness) are only intended for new projects. Then again, this is a long-term wish list, we all know that Java isn't exactly the fastest-moving language out there when it comes to the introduction of new features.

Extension methods: I can only talk about the Xtend-case here, I've yet to see this feature in another language working in this fashion. In Xtend, you still have to import the utils classes you want to use; that alone limits the room for misunderstandings. In the Xtend IDE, the call to an extension method is highlighted slightly differently than a regular method call, so it's always clear what you are calling. If in doubt, a quick "jump to declaration" will give a definitive answer to what is being called here. Here's another example why extension methods make life so much easier:

// Java 8 (ok, but super verbose)
List<String> lowercased = myStrings.stream().map(String::toLowerCase).collect(Collectors.toList());

// Java 8 with static utils
List<String> lowercased = CollectionUtils.map(myStrings, String::toLowerCase)

// with Xtend-style extension methods:
List<String> lowercased = myStrings.map(String::toLowerCase);

Strict null-ness: The way this works in Ceylon under the hood is by using so-called Union Types. null is not assignable to any class in Ceylon, other than Null (i.e. it has it's own class). When you define a parameter as User?, it is a shorthand for User | Null, i.e. "this paramter has to be a User or null". A method call on a variable of a union type is only valid if all type members of the union support the method. Any method call, e.g. getUsername(), is invalid on a parameter variable of type User?, because Null defines no methods. So how do you transform User? into User? Flow typing has you covered:

   User? user = userService.loadUserById(1234);
   // user.getUsername() is illegal here because of union type with NULL
   if(user != null){
      // flow typing narrows union type down from (User | Null) to just User...
      user .getUsername(); // ... so this is ok here
   }

Similar concepts were introduced e.g. in TypeScript. And yes, this check can break existing code bases. It's an opt-in compiler feature and migrating an existing code base is not always easy. However, I've seen an interesting behaviour in programmers. Let's say method a(obj) calls b(obj), and b(obj) in turn calls c(obj). c cannot deal with null parameters. Therefore, b will get a compiler error if it tries to pass it's nullable parameter into c. As a consequence, programmers tend to forbid the parameter of b to be nullable. Then a has an error, and so on. The treatment of nulls is shifting automatically and intuitively to where it belongs: towards the I/O handling at the system boundary.