3

Hi Question is simple.

What is difference between String::startWith and ""::startWith ?

Hasnain Ali Bohra
  • 2,130
  • 2
  • 11
  • 25

1 Answers1

6

String::startWith applies the startWith() method to the first argument of the lambda.

""::startWith applies the startWith() method to a the "" literal or in a broader way to a variable that is not declared as a lambda argument.

To be more exhaustive, these two ways of providing a method reference are not substitutable.

Suppose that you want to use method reference for the public boolean startsWith(String prefix) method.
I specify it as the method is overloaded.

The method reference using a lambda parameter is designed to work with a BiPredicate<String, String> functional interface while the method reference using a variable not declared in lambda parameters is designed to work with Predicate<String> functional interface.

  • The way with a variable passed as the target of the method reference:

    String myVariable; myVariable::startWith

provides already the String on which the method reference should be applied. Here : myVariable.
So, only the prefix parameter needs to be passed in the lambda.
So Predicate<String> suits.

  • The way with using the first argument of the lambda parameter as the target of the method reference:

    String::startsWith

doesn't provides the String on which the method reference should be applied.
So, both the String on which the method should be invoked and the prefix parameters need to be passed in the lambda.
So BiPredicate<String, String> suits.

Here is a sample code to illustrate:

public static void main(String[] args) {

    // using method reference with lambda parameter
    myMethodWithBiPredicate((s, prefix) -> s.startsWith(prefix), "mystring", "my");
    myMethodWithBiPredicate(String::startsWith, "mystring", "my");

    // using method reference with variable not in lambda parameter
    String stringNotInLambdaParams = "stringNotInParam";
    Predicate<String> functionPredicate = stringNotInLambdaParams::startsWith;

    System.out.print("myMethodWithBiPredicate with string "
            + "(included in the method reference)=" 
            + stringNotInLambdaParams 
            + " and prefix= string | Result = ");

    myMethodWithPredicate(functionPredicate, "string");

}

public static void myMethodWithBiPredicate(BiPredicate<String, String> function,
         String string,
         String prefix) {

    System.out.println("myMethodWithBiPredicate with string=" 
            + string + " and prefix= " + prefix 
            + " | Result = " + function.test(string, prefix));
}

public static void myMethodWithPredicate(Predicate<String> function, String prefix) {
    System.out.println(function.test(prefix));
}

that produces this output :

myMethodWithBiPredicate with string=mystring and prefix= my | Result = true

myMethodWithBiPredicate with string=mystring and prefix= my | Result = true

myMethodWithPredicate with string (included in the method reference)=stringNotInParam and prefix= string | Result = true

Leonardo Pina
  • 458
  • 1
  • 7
  • 17
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • How wonderfully confusing for the designers to use that syntax. Thanks for the explanation regardless. – Aluan Haddad Jul 20 '17 at 12:23
  • 4
    @Aluan Haddad: I don’t see anything confusing here. `object::method` will invoke `method` on `object`. `Class::method` has no object, hence, the target object becomes the first functional parameter, if `method` is not `static`. In other words, `String::startsWith` is equivalent to `(a,b) -> a.startsWith(b)` and `""::startsWith` is equivalent to `b -> "".startsWith(b)` (which is equivalent to `b -> b.isEmpty()`) – Holger Jul 20 '17 at 12:28
  • What is confusing to me, is the use of the `::` to resolve static and instance members, eschewing the `.` in both cases. From a C++ point of view, `::` would resolve a static member and `.` would resolve an instance member. From a C# POV, `.` would resolve either. Of course we are not talking about C++ or C#, but it is an odd choice in my view. – Aluan Haddad Jul 20 '17 at 12:32
  • 5
    @Aluan Haddad: forget the meaning of `::` in other languages. Consider that in Java, accessing a member always worked via `.` (dot) in Java, regardless of whether the member is `static` or not. Using `::` (double colon) instead of the dot in Java doesn’t change the way the method is resolved, instead, it implies that the method is not invoked directly, but bound to a function that will invoke the method. – Holger Jul 20 '17 at 12:37
  • 2
    @Aluan Haddad The syntax is not so confusing even if it may be disconcerting but the usage is really different. I added a sample to illustrate that these are not interchangeable. – davidxxx Jul 20 '17 at 13:23
  • @davidxxx thank you for that your explanation is very thorough and clear. They two method references do not have the same arity, regardless how they are acquired, and so there is no ambiguity because, while Java allows static members to be invoked on instances, it adjusts resolution of `static` methods when a method is overloaded has an instance and a static overload. Is that correct? – Aluan Haddad Jul 20 '17 at 13:38
  • @Aluan Haddad You are welcome. Interesting subject. "They two method references do not have the same arity, so there is no ambiguity ". Right .But it has no relation with `static` modifier. `startsWith` is an instance method. And in both cases of my example, it is the same instance method that is invoked (`public boolean startsWith(String prefix)`). In Java, declaring a method with a static method with a specific signature will not compile (not valid overriding) if you remove only the `static` modifier. To overload, which has to change are the method parameters. (1/2) – davidxxx Jul 20 '17 at 18:59
  • A method reference may also be used with a static method and there is no ambiguity with an instance method because as explained the static or instance (default if static is not specified) modifier will not allow to override the method. So you will not have any case for a method with a specific signature that would be both static and no static in a method reference(2) – davidxxx Jul 20 '17 at 19:03
  • 1
    @davidxxx: but you can have a `static` method and an instance method with the same name, but different signature, whose functional signature is identical, making a method reference ambiguous, like in [this example](https://stackoverflow.com/q/21873829/2711488). – Holger Jul 20 '17 at 20:45
  • @Holger This is indeed possible. Good post. It is another case but you are right to mention it. Because as in a general way, method invocation ambiguity for the compiler is still possible with method reference. Finally as for classic method invocations. – davidxxx Jul 20 '17 at 20:55
  • @davidxxx the solution you provided is bit confusing – Hasnain Ali Bohra Jul 21 '17 at 05:57
  • @Hasnain Ali Bohra Why ? Anyway, it is not a solution but a concrete example of how the two can be used. – davidxxx Jul 21 '17 at 06:06
  • @davidxxx thanks for your respond.I have simple question though **""::startWith** and ** String::StartWith** is used only for return the implementation for startWith to the lamda.Right so y we are concerning about the parameters. – Hasnain Ali Bohra Jul 21 '17 at 06:08
  • @Downvoter can you explain how could I improve my answer ? – davidxxx Jul 21 '17 at 06:09
  • @Hasnain Ali Bohra Not exactly. `""::startWith` or `String::StartWith` are shortcuts to write a lambda that itself is a shortcut to write an anonymous class for a interface that has a single abstract method declared, or shortly said a functional interface. But `""::startWith` or `String::StartWith` cannot be used for the same functional interface as the first one defines already the target object of the lambda (an empty String : `""`) and the second doesn't. – davidxxx Jul 21 '17 at 06:31
  • @davidxxx thanks for you response. I have certatain question regarding this . hasnain.ali.bohra52@gmail.com – Hasnain Ali Bohra Jul 21 '17 at 06:35