Home > Mobile >  Java double colon (::) operator equivalent in Scala
Java double colon (::) operator equivalent in Scala

Time:10-11

I've seen a few examples of how to write :: equvalant in Scala, for eg passing System.out::println to a function, but I'm unable to figure out how to write this Vaadin Grid component in Scala:

Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");

Here the get methods are regular getters, and not a static methods. Any idea how can this code be written in Scala 2(.12)?

Edit: The java code above (grid.addColumn(..)) calls this specific method:

public Column<T> addColumn(ValueProvider<T, ?> valueProvider) {
        BiFunction<Renderer<T>, String, Column<T>> defaultFactory = getDefaultColumnFactory();
        return addColumn(valueProvider, defaultFactory);
    }

ref: https://github.com/vaadin/flow-components/blob/2f6bce42b67651fd47d202fa83d8359c619fe099/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java#L1625

CodePudding user response:

It depends on the shape of the method and, in some cases, the version of Scala you're using. Scala 3 made some pretty significant changes to the way methods work, so some of the syntax works differently.

Let's assume we've got these signatures.

def foo(n: Int): Int
def bar(a: Int, b: String): Int
def baz(): Int
def frobnicate: Int

First, if you're in Scala 3 and the method takes mandatory arguments, then you can simply use a . and get the method as a first-class object. So for each of the following methods, you can use this trick.

In that case, myInstance.foo is a function of one argument and myInstance.bar is a function of two. You cannot use this trick with a function of zero arguments, even in Scala 3.

On the other hand, if the method has no arguments, or if you're working in Scala 2 (which came out before this syntax was finalized), then you can convert a method to an object by placing an underscore (not in parentheses) after it. For example, foo _ is a function of one argument in both Scala 2 and 3. bar _ is a function of two arguments, and baz _ is a function of zero arguments. frobnicate _ is a syntax that used to work in Scala 2, but it won't work anymore for functions of zero arguments declared without parentheses in Scala 3. In Scala 3 for such functions, you'll have to wrap them in an explicit lambda.

You can also (for functions that take one or more arguments) explicitly place underscores in the argument positions. Sometimes this is more readable, and it's required if there are multiple overloads of different arities. So, in both Scala 2 and 3, foo(_) is a function of one argument, while bar(_, _) is a function of two, and we could even partially apply bar as bar(0, _) or bar(_, "")

In summary, assuming the signatures listed at the top of this answer:

Code Scala 2 behavior Scala 3 behavior
obj.foo Error (Int) -> Int
obj.bar Error (Int, String) -> Int
obj.baz Calls baz() Error
obj.frobnicate Calls frobnicate Calls frobnicate
obj.foo _ (Int) -> Int (Int) -> Int
obj.bar _ (Int, String) -> Int (Int, String) -> Int
obj.baz _ () -> Int () -> Int
obj.frobnicate _ () -> Int Error
obj.foo(_) (Int) -> Int (Int) -> Int
obj.bar(_, _) (Int, String) -> Int (Int, String) -> Int
() => obj.baz() () -> Int () -> Int
() => obj.frobnicate () -> Int () -> Int

So if you want to be maximally backwards compatible, then the safest thing to do is to put an _ after the function name, as in obj.foo _. This will work in every case except Scala 3 functions of zero arguments which are declared with no parentheses (i.e. frobnicate in our example). For those, you'll need an explicit lambda, as () => obj.frobnicate

  • Related