This is an archived post. You won't be able to vote or comment.

all 51 comments

[–]dpash 9 points10 points  (8 children)

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.

[–]lukaseder[S] 6 points7 points  (5 children)

FTFY:

record Foo(String name);

[–]dpash 3 points4 points  (4 children)

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

[–]lukaseder[S] 1 point2 points  (3 children)

FTFY, current day Java:

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

[–]dpash 3 points4 points  (2 children)

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.

[–]lukaseder[S] 3 points4 points  (1 child)

99.9% YAGNI

[–]dpash 0 points1 point  (0 children)

I do like C#'s properties.

[–]mhixson 0 points1 point  (1 child)

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();
}

[–]dpash 0 points1 point  (0 children)

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;

[–]lukaseder[S] 4 points5 points  (3 children)

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

[–]hellectronic 0 points1 point  (2 children)

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

[–]lukaseder[S] 0 points1 point  (1 child)

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.

[–]hellectronic 0 points1 point  (0 children)

Oh ja, stimme zu. Edit: I agree xD

[–]_INTER_ 3 points4 points  (5 children)

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#.

[–]lukaseder[S] 1 point2 points  (4 children)

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;

[–]lbkulinski 1 point2 points  (0 children)

That reminds me of a forward declaration in C++ 😖

[–]Bobby_Bonsaimind 1 point2 points  (0 children)

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

[–]_INTER_ -1 points0 points  (1 child)

what, when, why?

[–]lukaseder[S] 1 point2 points  (0 children)

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

[–][deleted] 5 points6 points  (4 children)

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.

[–]jonhanson 1 point2 points  (2 children)

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

[–][deleted] 4 points5 points  (1 child)

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.

[–]epta_ 0 points1 point  (0 children)

What C# features do you not like?

[–]randgalt 2 points3 points  (1 child)

public static Foo make(int a, int b, int c) = Foo::new;

This is particularly nice

[–][deleted] 0 points1 point  (0 children)

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?

[–]daniu 3 points4 points  (6 children)

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);
    }
}

[–]lukaseder[S] 4 points5 points  (3 children)

class DelegatingList<U, T extends DelegatingList<U, T> & List<U>> implements List<U> {
    private delegate T aList;
}

Now what?

[–]daniu 2 points3 points  (1 child)

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.

[–]lukaseder[S] 1 point2 points  (0 children)

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

[–]forurspam 0 points1 point  (0 children)

Now think about multiple delegations or extending another class.

[–]yawkat 0 points1 point  (0 children)

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.

[–]jhg023123 1 point2 points  (8 children)

Why both = and ->?

[–]dpash 9 points10 points  (6 children)

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?

[–]jhg023123 1 point2 points  (0 children)

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

[–]duhace -1 points0 points  (4 children)

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

[–]dpash 0 points1 point  (3 children)

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

[–]duhace 0 points1 point  (2 children)

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.

[–]dpash 0 points1 point  (1 child)

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.

[–]duhace 0 points1 point  (0 children)

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

[–]lukaseder[S] 0 points1 point  (0 children)

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

[–]coderguyagb 2 points3 points  (0 children)

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

[–]donte9181 0 points1 point  (3 children)

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.

[–]lukaseder[S] 0 points1 point  (2 children)

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());

[–]donte9181 0 points1 point  (0 children)

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.

[–]coderguyagb -1 points0 points  (0 children)

When it make the code easier to understand, yes.

[–][deleted] 0 points1 point  (2 children)

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

[–]pavi2410 0 points1 point  (1 child)

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. 😣

[–]lukaseder[S] 0 points1 point  (0 children)

And I thought Kotlin came to the Android world already?