r/java Sep 19 '18

JEP draft: Concise Method Bodies

http://openjdk.java.net/jeps/8209434
57 Upvotes

51 comments sorted by

10

u/dpash Sep 19 '18 edited Sep 19 '18

While this will make getters much concise, it won't help much with setters, because they're a statement.

private String name;
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

would become

private String name;
public String getName() -> name;
public void setName(String name) -> this.name = name;

I'm curious to see how their introduction in C# changed the language. My initial feeling is that the usefulness of this change might be of limited scope, if only because only the most basic of methods would benefit from this.

Edit: I originally had the following setter

public void setName(String name) -> { this.name = name; }

but it was pointed out I had needless braces.

6

u/lukaseder Sep 19 '18

FTFY:

record Foo(String name);

4

u/dpash Sep 19 '18 edited Sep 19 '18

If and when we get data classes. Also, if and when we get this JEP :)

3

u/lukaseder Sep 19 '18

FTFY, current day Java:

public class Foo {
  public String name;
  public Foo(String name) { this.name = name; }
}

5

u/dpash Sep 19 '18 edited Sep 19 '18

From what I remember of the data class document, the fields don't quite have public access, but can be accessed like they do. I think it was possible to create a custom setter or getter if you need to customise it.

4

u/lukaseder Sep 19 '18

99.9% YAGNI

1

u/dpash Sep 19 '18

I do like C#'s properties.

1

u/mhixson Sep 19 '18

It's not clear to me from the JEP that your setter would require curly braces. The lambda form doesn't need braces. This works, for example:

static int x;
void foo() {
  Runnable r = () -> x = 1;
  r.run();
}

1

u/dpash Sep 19 '18 edited Sep 19 '18

I guess you're right, because assignment is an expression, not a statement as I originally said.

public void setName(String name) -> this.name = name;

5

u/lukaseder Sep 20 '18 edited Sep 20 '18

I couldn't resist. And the answer I got is quite promising, in fact!

1

u/hellectronic Sep 21 '18

nested "named lamdas"/methods would be really nice !

1

u/lukaseder Sep 21 '18

Absolutely. I really like Brian Goetz's answer to this, in particular that there's a lot of nesting that is currently quite clumsy for arcane reasons, which could be cleaned up. I totally agree that this should be done in a separate project, though.

1

u/hellectronic Sep 21 '18

Oh ja, stimme zu. Edit: I agree xD

5

u/_INTER_ Sep 20 '18

I always thought that Java's code readability code was rather nice. Mainly because the structure of classes, methods, many control structures always remain the same visually. You can usually scan a class easily without much interruption. It's modifier, return type / void method name, parameters, brace, body, brace.

Now this feature completely destroys that. It adds new syntax without much gain. The languages keeps on adding and bloating, like C++ and C#.

2

u/lukaseder Sep 20 '18

If that's what you think, then you will like the idea that soon empty class bodies can be replaced by semi colons. Instead of:

class A {}

Just write

class A;

2

u/lbkulinski Sep 20 '18

That reminds me of a forward declaration in C++ šŸ˜–

2

u/Bobby_Bonsaimind Sep 21 '18

Give me a moment, I'll try to figure out if I could care less... ... ...nope, sorry.

0

u/_INTER_ Sep 20 '18

what, when, why?

2

u/lukaseder Sep 20 '18

See e.g. http://cr.openjdk.java.net/~briangoetz/amber/datum.html

interface Node { }

abstract record BinaryOpNode(Node left, Node right) 
    implements Node;

record PlusNode(Node left, Node right) 
      extends BinaryOperatorNode(left, right);

record MulNode(Node left, Node right) 
      extends BinaryOperatorNode(left, right);

record IntNode(int constant) implements Node;

And also: http://hg.openjdk.java.net/amber/amber/rev/727ca44aff68, https://twitter.com/jeffreymaxwell/status/984079813941125120

I've never seen a formal specification, though. Seems like a simple "under the hood" change to the JLS

7

u/[deleted] Sep 20 '18

Okay, C# programmer here; please don't implement this crap. C# has all kinds of 'neat' features that have made the language very nasty to work with and is driving some people away (like myself). You think it's a great feature until another developer bastardizes the crap of of them and then shoves it all together. Remember that, where we can choose to or not to use a feature, that isn't true about the code you have to maintain or the devs you have to work with. I am looking to switch to Java for its elegance in its simplicity. But if they decide to start adopting the features of C#, I'll go somewhere else.

2

u/jonhanson Sep 20 '18

Preventing mis-use by idiots isn't generally a good guiding criteria for language design.

4

u/[deleted] Sep 20 '18 edited Sep 20 '18

No it isn't. But coming from C#, this feature is not one that yields more readable or maintainable code. Once you start to open the flood gates then you get all kinds of crap...take a look at what they've done and have proposed with C#. I'm not saying that all features are bad but I am saying that we need to be extremely cautious about what gets added. It seems that languages are too focused on 'advancing' just to not be stale and forgetting the purpose of what the language was designed to do and the problems it was designed to solve. I don't want a language that is everything to everyone. I don't want my imperative, OO language turning into a landfill of pointless buzzwords/syntax sugar and littered with so much functional language idioms that it's forgotten what audience it is trying to serve. If I wanted a functional language, I would use one. I choose to use Java because of what it is and what it isn't.

1

u/epta_ Sep 29 '18

What C# features do you not like?

3

u/randgalt Sep 20 '18
public static Foo make(int a, int b, int c) = Foo::new;

This is particularly nice

1

u/[deleted] Sep 20 '18

Haven't read the article yet, this can be useful for a "quick and dirty" but extendable factory method, making the constructor protected/private, isn't it?

6

u/daniu Sep 19 '18 edited Sep 19 '18

Sure, getters and setters are somewhat shorter by this, but if you're annoyed by those and not using Lombok I can only assume it's due to your company policy. I can't see how non-trivial methods gain a lot by being able to remove the braces surrounding the body. Not that I'm against the introduction of this feature, it does make it more consistent with the lambda stuff, but I don't see it as a huge improvement.

class MyList<T> implements List<T> {

   private List<T> aList;

   public int size() = aList::size;
   public T get(int index) = aList::get;
   ...
}

That's actually kind of common - delegating the implementation of an interface to a member. But it's not really that much less boilerplate if you still have to delegate on a method level - there could be something like

class DelegatingList<T> implements List<T> {
    // 'delegate' meaning all non-implemented List methods are created for this class and delegated to aList
    private delegate List<T> aList;

    public void add(T item) {
        // still able to implement specific methods of the interface yourself
        aList.add(item);
    }
}

5

u/lukaseder Sep 19 '18
class DelegatingList<U, T extends DelegatingList<U, T> & List<U>> implements List<U> {
    private delegate T aList;
}

Now what?

3

u/daniu Sep 19 '18

Now you've made the point about how actual language design isn't trivial ;)

I was merely bringing it up as something in the vein of the concept of the JEP draft I stumble upon from time to time.

2

u/lukaseder Sep 20 '18

And I merely wanted to nerd snipe you, hoping you'd spend a few hours finding the solution ;-)

1

u/forurspam Sep 20 '18

Now think about multiple delegations or extending another class.

1

u/yawkat Sep 20 '18

This sort of delegation has all the problems normal extension has, except for being able to swap out impl. Don't do it, even though lombok allows you to.

I wrote on the problems with extending like that a while ago: https://javachannel.org/posts/how-not-to-extend-standard-collection-classes/ - the proper solution is to just exhaustively implement abstractlist.

2

u/jhg023123 Sep 19 '18

Why both = and ->?

10

u/dpash Sep 19 '18

Because they do different things. = is saying that the method is the same as the method handle, and will return what ever the original method returns. -> would return a method handle (and ignore the parameter).

public boolean isEmpty(String s) = String::isEmpty;
public Predicate<String> isEmpty(String s) -> String::isEmpty; 

Does that make sense?

2

u/jhg023123 Sep 19 '18

Oh, I never thought about it like that. Thanks!

0

u/duhace Sep 20 '18

the second example should do like scala instead

 //Predicate[String] == String => Boolean in scala
 def isEmpty: String => Boolean = String.isEmpty _

i'm not sure why the parameter s is even there if it's being ignored

1

u/dpash Sep 20 '18

Because using -> is the wrong thing to use in that situation.

1

u/duhace Sep 20 '18

I'm not sure I'm getting the difference then. In my code, i'm doing the equivalent of returning the method handle as you said, not using the isEmpty method as the implementation like 1.

1

u/dpash Sep 20 '18

Of my two examples, the second is almost certainly a mistake, and you'd normally never write that. In particular because it ignores the parameter, making the method confusing for any users.

1

u/duhace Sep 20 '18

would you post what you would write to clear things up for me? thanks in advance

1

u/lukaseder Sep 20 '18

Important to note also that specific syntax is probably not set in stone at this moment.

3

u/coderguyagb Sep 20 '18

More unmaintainable crap. Don't do this. It's hard enough to read the legacy codebases I see every day that some 'Streamify / Kotlin all the things' developer has been near. /rant

1

u/donte9181 Sep 21 '18

Shorter != better.

This feels like it's solving a non-problem. Not once have I ever felt that I was unable to complete a project on time because I had to type some curly braces for trivial methods. On top of that, I don't even type them - my IDE does, so I actually type the same number of characters the "long way" today as I would in the "short way" described here, so it doesn't affect my coding velocity at all.

What it does do is make code significantly harder to read/understand because now you're overloading the arrow/method-reference notation. Currently it's limited to method invocation but now you're suggesting that it could be used while defining methods, too. It requires extra (unnecessary) brain cycles to figure out where the method definition ends and what it's actually doing - especially in the examples w/ multiple arguments.

It's unnecessary complexity that adds to the amount of mental cycles required to consume existing Java code - which is 90% of our jobs. This will make reading Java code harder while having 0 effect on our ability to write code efficiently.

This seems like one of those things someone suggested because they saw something similar in another language and thought it was cool, but will only make Java worse. Keep Java simple. Keep Java readable. Let your IDE write the boilerplate for you.

1

u/lukaseder Sep 21 '18

So, you don't use lambdas but keep writing anonymous classes?

Stream.of(1, 2, 3)
      .filter(new Predicate<Integer>() {
          @Override
          public boolean test(Integer i) {
              return i % 2 == 0;
          }
      })
      .map(new Function<Integer, String>() {
          @Override
          public String apply(Integer i) {
              return i.toString();
          }
      })
      .collect(toList());

1

u/donte9181 Sep 21 '18

I do, but that is not even close to an equivalent comparison. Anonymous inner classes make it really hard to find the code that actually provides the meaning as to "what is this code trying to do". You have to sift through multiple lines of annotations and additional method definitions just to get to the "what does this do" whereas lambdas are much more direct.

This is not the case with the "problem" that this draft attempts to solve:

public String getName() { return name; }
public String getName() -> name;

There's no scouring the code for the actual logic of the first function. Your not sifting through extra crap the compiler needs to stay type safe and avoid warnings like you do with inner classes. It's just replacing curly braces with arrow notation or double colons. You basically got to get rid of the word 'return'. That's not that big of a win to me given that it will increase the mental gymnastics required to read Java code.

It's not better - it's just different - and we don't get anything interesting as a result of having yet another way to define functions.

If the community wants to take a swag at first-class properties like C# and the syntax is something like this, I'm all ears. But this just seems like trying to convince people that the barrier between them and great code is a couple of curly braces.

0

u/coderguyagb Sep 21 '18

When it make the code easier to understand, yes.

1

u/[deleted] Sep 23 '18

I’d be happier if they introduced Case classes like those in Scala. I tend to use Lombok/Immutables to accomplish the same thing.

1

u/pavi2410 Sep 24 '18

Java will soon become Kotlin-ish with the 'switch expression' proposed for Java 12 which I love to see it coming. But, this would only matter me if newer Java versions come to the Android world. 😣

1

u/lukaseder Sep 24 '18

And I thought Kotlin came to the Android world already?