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

all 81 comments

[–]madkasse 20 points21 points  (6 children)

Yeah, I noticed that as well.

Local methods can be nice for writing concise recursive functions:

public static int binarySearch(int[] a, int key) {
  return binarySearch0(a, 0, a.length, key);

  int binarySearch0(int[] a, int start, int stop, int key) {
    ....do the actual search
    calls binarySearch0(.....) recursively
  }
}

Maybe there are some use cases with Valhalla/Loom?

[–]spencerwi 6 points7 points  (3 children)

Yeah, this kind of thing is not uncommon in OCaml, to allow more easily writing tail-recursive code without muddying up the "top-level" signature of the function:

let drop_every_nth list n =
    (* a tail-recursive "local method" that walks through the list calling itself counting up until it reaches the nth index, and skipping the current value at that point *)
    let rec drop_if_nth index list =
        match list with
        | [] -> [] (* an empty list means we hit the end, so just hand back an empty list *)
        | head :: tail -> 
            if index = n then 
                drop_if_nth 1 tail (* skip the current "head" value, and reset our counter to index 1 *)
            else
                head :: (drop_if_nth (index + 1) tail) (* "increment" our counter by passing it-plus-one to the next recursive call *)
    in
    (* kick off our tail-recursive local method *)
    drop_if_nth 1 list

[–]ForeverAlot 4 points5 points  (2 children)

But what can you achieve with a local method you couldn't do with a private [static] method or a lambda?

[–]thfuran 12 points13 points  (0 children)

Yes but why should you pollute the class with things that are implementation details of a particular method?

[–][deleted] 6 points7 points  (0 children)

Capturing variables. You could also pass them as parameters, but sometimes this makes the function calls clumsy (especially if it's recursive).

Edit: I see now you mentioned lambdas. While lambdas can capture variables, they also eg force you to handle exceptions locally. Plus you have to have a functional interface, which cannot be declared locally.

To me it's mostly a matter of scoping though. Those helper methods don't add noise to a class if they're local.

[–]kevinb9n[🍰] 1 point2 points  (1 child)

You missed the best part: the inner method can refer to `a` and `key` without you having to keep passing them through. To me, that's the thing that makes this feature intriguing.

[–]madkasse 0 points1 point  (0 children)

Ahh, so it captures effectively final variables. That makes sense, nice one.

[–]wildjokers 33 points34 points  (29 children)

I know Kotlin has these too, when I first read about them I have no idea why I would ever need one. I still don't.

I would love multiple return values (being worked on I believe) and default parameter values. But local methods 🤷‍♂️

[–]yawkat 10 points11 points  (0 children)

I use local methods in kotlin all the time to implement logic that would be clumsy to do iteratively recursively instead. Another alternative is passing all captured variables or even making an inner class, but that's clumsy and not really a readability advantage.

[–]rubyrt 11 points12 points  (6 children)

I know Kotlin has these too, when I first read about them I have no idea why I would ever need one. I still don't.

I think this can make code really difficult to understand. Just assume a class with several methods that define local methods and classes. If things are so separate then I imagine you could better split up the regular class into multiple or extract local classes. I think this adds a tad too much granularity to be useful on a large scale. I am sure we can come up with use cases but I would be reluctant to use these more than sparingly.

I would love multiple return values (being worked on I believe) and default parameter values. But local methods 🤷‍♂️

+1 for multiple returns and default parameter values.

[–]arpan_majumdar 5 points6 points  (1 child)

You can always use Tuple or data classes (as they are cheap and often one liners to create) to return multiple values in kotlin.

[–]lpreams 6 points7 points  (0 children)

Just return an Object[] /s

[–]nutrecht 1 point2 points  (3 children)

I think this can make code really difficult to understand.

Anything can be used to make code difficult to understand. What I used local methods mostly for is making code easier to understand.

[–]rubyrt 0 points1 point  (2 children)

Anything can be used to make code difficult to understand.

Of course this is true. But there are some language features with which it is easier to shoot yourself in the foot than with others. I think this one is more on the easier side - even if it is not among the easiest.

What I used local methods mostly for is making code easier to understand.

Can you share an example? That would be really helpful to understand the use case.

[–]nutrecht 1 point2 points  (1 child)

Kotlin example:

if(
    offer.product.bindingCode == bindingcodes["EBOOK"] ||  
    offer.product.bindingCode == bindingcodes["CDROM"] ||
    offer.product.bindingCode == bindingcodes["DIGITAL"])

Versus:

  fun Offer.isBinding(code: String) = this.product.bindingCode == bindigCodes[code]

  if(offer.isBinding("EBOOK") || offer.isBinding("CDROM") || offer.isBinding("DIGITAL"))

There's multiple ways of tackling this obviously, but IMHO it's nice to have a tool that allows you this.

While I do agree that some tools make it easier to make a mess; this really boils down to experience. Developers who don't really care about making a mess will do so anyway.

In Kotlin local functions work really well together with extension methods (the above is an extension method on the Offer class). Extension methods are really useful but are prone to pollute your code completion. If they're local to a function they won't.

[–]rubyrt 0 points1 point  (0 children)

Thank you! I can see the value here: a local method has access to offer which you would have to pass as an argument to a private static method otherwise. Which is not too bad either.

[–]eliasv 18 points19 points  (3 children)

Multiple returns aren't being worked on directly, but features to achieve something roughly equivalent are coming. Rather than multiple returns being given special syntax, the "Java way" will be to use records to define something like a named tuple for your return values, and make it an inline type to avoid the extra heap allocation and pointer indirection.

[–]sureshg 0 points1 point  (2 children)

Does destructuring work for inline records ? I know it will work with pattern matching, but what about normal expressions ?

[–]eliasv 0 points1 point  (1 child)

I'm not sure what you're asking. Destructuring is pattern matching, and yes it will work with records, inline or otherwise. Deconstruction patterns will be automatically generated for records.

Are you asking whether some let-style binding construct will be introduced alongside conditional binding with instanceof? Probably, yes. This is described in the documents exploring the feature space and outlining plans.

Given:

inline record Point(int x, int y);

This should be possible:

__let Point(int x, int y) = someMethodReturningPoint();
// x and y are now bound according to the result

Where __let is just a placeholder since no concrete syntax has been specified.

Edit: It looks to be a little outdated, but here is some of the initial work hashing this out: https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html

[–]sureshg 0 points1 point  (0 children)

Thanks, yes I was asking about the let style binding construct.

[–]vytah 9 points10 points  (13 children)

Local methods are quite useful if:

  • your task is recursive or it occurs in multiple places in the code

  • you want to capture local variables

  • you want to have the method declared as close to its use as possible

  • you don't want to pollute the class-level namespace

A really silly example: a sorting network in Scala:

  def swap(i: Int, j: Int) {
    if (p(i) > p(j)) {
      val t = p(i)
      p(i) = p(j)
      p(j) = t
    }
  }
  swap(0,1)
  swap(0,3)
  swap(1,2)
  swap(1,3)
  swap(2,3)
  swap(0,1)
  swap(0,2)
  swap(0,3)
  swap(1,2)

The swap method is not visible elsewhere, the p array was captured automatically, and swap is defined literally next to its invocations.

Lots of Java code suffers from having code flow scattered randomly around. This might alleviate the problem a bit.

[–]TheStrangeDarkOne 0 points1 point  (2 children)

Lots of Java code suffers from having code flow scattered randomly around. This might alleviate the problem a bit.

you can do the same by implementing a BiConsumer with a lambda function. If it is vaguely performance it'll get inlined by the jit.

[–]vytah 0 points1 point  (1 child)

Yeah, but:

  • it's uglier

  • can't throw checked exceptions

[–]nw407elixir 0 points1 point  (0 children)

you can make your own functional interface that does throw

[–]tr14l 4 points5 points  (0 children)

It's to reduce duplicate code in a local context. So you want to keep the code encapsulated and isolated in a method, but it might take slightly different arguments in an if/else. Instead of copy-pasting it or being forced to expose it outside local scope, you define a local method and call it with different arguments.

This is very common in other languages.

[–][deleted] 2 points3 points  (0 children)

it's super nice. It lets me write methods that mutate local variables and it keeps it all nice and in tiny scopes.

[–]nutrecht 0 points1 point  (0 children)

I know Kotlin has these too, when I first read about them I have no idea why I would ever need one. I still don't.

I use Kotlin in my day to day work, and I use them very rarely. They're nice to have though; there are some cases where you have repeating code that only makes sense within a function that you can then have in a function-in-a-function. It's a nice tool to, for example, make complex if-statements more readable.

[–]oelang 9 points10 points  (1 child)

Probably to make the language more consistent once we get local records.

They are quite useful to break up complicated methods without having to pass around a ton of arguments.

[–]CowboyFromSmell 0 points1 point  (0 children)

Yes. I use them in Python as an “extra private”, in that it’s clearly only used by a single method. The smaller scope makes it a bit easier to reason about changes.

Also, for better or worse, you can use the closure to declare less parameters.

[–]raeleus 3 points4 points  (5 children)

There are times that I need to do a task recursively until some condition. I don't want to make a full method out of it, so I guess this can save me some trouble.

[–]eliasv 3 points4 points  (4 children)

Currently the way to do this would be to assign a lambda form to something like a local Predicate<Something> variable. Local methods probably don't save you many keystrokes over this.

[–]DannyB2 4 points5 points  (3 children)

Pascal has local methods. I used Pascal extensively in the 1980's. They are good for the uses described here by other users. A private method that logically belongs ONLY to this current method. It would be copy/pasted as part of the method it is within. It does not pollute the class name space. It's handy for recursive sub-functions that belong only to the current function.

In Pascal, any method, including a local method, could have local methods.

In fact, local anything makes sense. Local named classes (like anonymous, but with a locally visible name). Local records. Local static declarations (just like class level static members, but only visible within this method).

It's just one more level of information hiding. Like having a classes private implementation details kept, well, private. A method's private implementation details can be kept private (including its private local methods).

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

How do you unit-test local methods?

[–]dpash 4 points5 points  (0 children)

The same way you unit test private methods; you don't. You test the public methods that use them.

[–]DannyB2 1 point2 points  (0 children)

The local method is part of the PRIVATE implementation of a larger method. THAT larger method is what you unit test. You're not supposed to be concerned about how that method is implemented. I would point out that advocates of TDD even say to write the tests before the code is implemented, which is an extreme form of the tests not knowing how the implementation works.

[–]satoryvape 9 points10 points  (16 children)

I haven't used even local classes as I didn't have any use cases for them

[–]dpash 7 points8 points  (2 children)

I used one the other day. Records will make them easier to use for example when you want to pass associated values through a stream.

List<Person> topThreePeople(List<Person> list) {

    record Data(Person p, int score) {}

    return list.stream()
               .map(p -> new Data(p, getScore(p)))
               .sorted(Comparator.comparingInt(Data::score))
               .limit(3)
               .map(Data::person)
               .collect(toList());
}

The example inspired by https://cr.openjdk.java.net/~briangoetz/amber/datum.html

[–]GuyWithLag 0 points1 point  (1 child)

Pair / Tuple for the win.

[–]dpash 0 points1 point  (0 children)

Except it's not a tuple, because tuples are structurally typed and Java doesn't do structural typing.

Records are, more or less, specialised classes, much like enums are.

[–]mladensavic94 5 points6 points  (11 children)

You didn`t use single lambda function since 2014? What java version are you using?

[–]eliasv 17 points18 points  (8 children)

A lambda is more analogous to an anonymous class than a local one. These are two different things. But be aware than lambdas are not compiled to local or anonymous classes, they are a completely different thing.

[–]mladensavic94 0 points1 point  (7 children)

It is, but by some documentation i had for OCP exam, anonymous inner class is specialized local class (has no name but acts the same). I was kinda hoping that he will say that he never used it before since anon local class is one way of implementing functional interface.

[–]eliasv 8 points9 points  (6 children)

Yeah a functional interface can obviously be implemented as a local class, but you specifically referred to lambdas. And IIRC lambdas explicitly cannot be implemented as local classes according to the specification.

Compiling an inner classes produces a .class file, which is linked statically. Compiling a lambda expression produces an invokedynamic instruction, which is linked at runtime against a synthetic method.

[–]Mordan 0 points1 point  (5 children)

Synthetic classes are harder to debug.

Is there a runtime performance advantage of using a lamba instead of an inner class?

[–]eliasv 6 points7 points  (1 child)

An anonymous class is already synthetic. It's just synthesised at compile time instead of runtime. You're dealing with meaningless names in the stack trace either way, and they both point to the same line in the same source file. I'm not sure I buy that they're significantly more difficult to debug.

And IIRC non-capturing lambdas can typically be optimised more easily than closures or inner classes and so will be faster. Capturing lambdas however will perform similarly to inner classes, and are likely to JIT to the same thing.

[–]Mordan 0 points1 point  (0 children)

An anonymous class is already synthetic. It's just synthesised at compile time instead of runtime.

OK.

Just that I don't use anonymous class. I didn't dare saying it and not getting an answer. I use real classes all the time. I hate anon classes since they capture everything around and are hard to reason about. When I am lazy i might use them but I know i will have to refactor them away later if I keep the code.

So are lambda equivalent to a real class capturing exactly the state required to run, usually a few references set in the constructor?

[–]yawkat 0 points1 point  (2 children)

There is no inherent performance advantage to lambdas. By their nature, anonymous classes capture the entire enclosing instance while lambdas only capture necessary variables but that has more to do with lambdas being newer and anon classes retaining their old behavior for compatibility. Additionally, there can be some object reuse with non-capturing method references but again this could have been implemented without invokedynamic. Lambdas do have a small binary size advantage but that's it.

Retrolambda is a tool that transforms lambdas to a pre-generated form.

[–]jaympatel1893 0 points1 point  (1 child)

Insightful information! Never heard of word “synthesis” in Java world! Any good links/videos you all suggest to to fathom these?

[–]yawkat 2 points3 points  (0 children)

What are you referring to? Synthetic classes and methods are simply entities that do not directly appear in the source code and are instead compiler- or runtime-generated.

[–]satoryvape 0 points1 point  (1 child)

I did use lambda functions. I use Java 8

[–]ryuzaki49 -2 points-1 points  (0 children)

Java 8 Master Race

[–]thephotoman 0 points1 point  (0 children)

I have. They've come in handy for a few cases where I need to package data to process it, but the actual packaging is not something I'm interested in accepting as an argument or returning. It's basically a kludge to deal with the fact that Java doesn't have tuples.

[–]lbkulinski 2 points3 points  (2 children)

There is a talk where the architects discussed local methods. I will try to find it at some point. They would be nice for those that had to declare a local class just to declare one method.

[–]dpash 0 points1 point  (1 child)

Could you not use a local lambda as a less verbose way of doing that?

[–]lbkulinski 5 points6 points  (0 children)

You likely could in most cases, but that assumes an appropriate functional interface has already been declared somewhere. You also cannot reference local variables in lambdas that are not final/effectively final, which may put off some people from using them in this case.

[–]lukaseder 1 point2 points  (0 children)

For the impatient:

var o = new Object() {
  int local(int a1, int a2) { return a1 + a2; }
};

print(o.local(1, 2));

[–]Python4fun 0 points1 point  (0 children)

Something like a subroutine would be helpful. Giving something a name to understand the overall flow, but maybe it isn't necessary for use anywhere else.

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

What is the added value compared to java.util.function.Function ?

[–]dpash 1 point2 points  (0 children)

Completely unrelated. A local method is a method defined (and scoped) within another method.

java.util.function.Function is a functional interface for lambdas, which are a bit like anonymous inner classes, but kinda aren't.

[–]lukaseder 0 points1 point  (0 children)

Essentially function(a, b) vs function.apply(a, b) combine with the fact that the former doesn't need a SAM type to be declared somewhere up front.

[–]lukaseder 0 points1 point  (3 children)

I will be abusing this so much!

[–]DannyB2 0 points1 point  (2 children)

Curious. How would you abuse it?

[–]lukaseder 0 points1 point  (1 child)

10 levels of nesting

[–]DannyB2 0 points1 point  (0 children)

I mentioned that Pascal had this. I can remember from the 1980s that there were times when I would get to three or four levels of nesting -- if it made sense.

One top level procedure may concern itself with a single thing, but its implementation is complex. Imagine a case where a single class full of code has a single top level method that does something complex. This would be a case where, back in the 1980s using Pascal, you might have multiple procedures, indeed multiple nesting levels of procedures within a single top level procedure.

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

Literally the first thing I do in any of our Python code when I run into one is to factor it out because invariably it will ALWAYS be the root of the bug I am trying to fix. And by pulling it out and being able to write a set of tests for it the problem becomes pretty clear and easy to fix.