Java 8 Functional interface assignment context

霸气de小男生 提交于 2021-02-07 12:43:06

问题


Question is regarding assignment context of functional interface-

Predicate<String> p = String::isEmpty;

Works fine where the isEmpty method declaration in String class is - public boolean isEmpty(){}.

If I try to declare same in custom class like -

class Test{
   public boolean isEmpty(){
       ...
   }
}

And make same assignment -

Predicate<String> p = Test::isEmpty;

It would be compilation error -

The type Test does not define isEmpty(String) that is applicable here

And Predicate<T> represents a predicate (boolean-valued function) of one argument and functional method is boolean test(T t){}.

Any explanation? And am I missing anything?


回答1:


You should have:

Predicate<Test> p = Test::isEmpty;

and not

Predicate<String> p = Test::isEmpty;

There is no String in

class Test {
    public boolean isEmpty(){
           ...
    }
}

so why should there be Predicate<String> ?

See the tutorial of method references. What you have here is the 3rd case "Reference to an instance method of an arbitrary object of a particular type".

Having

Predicate<String> p = String::isEmpty();
String s = "";

Calling p.test(s); is the same as callign s.isEmpty(); so that's why you cannot give as argument a String to call a method from Test.

It would have been possible to have a common Predicate if both String and Test would implement an interface Empty with the method boolean isEmpty() and then to have Predicate<Empty>. Then both p.test(string) and p.test(test) would work; otherwise it won't, Java has strong typing and does not support duck typing.




回答2:


The method reference can be used in a few ways. It can capture an instance on which to be invoked. It can refer to a method that should be invoked on the parameter provided by the functional interface (your first example). It can also be used as an invocation target where the parameter in the functional interface is passed as an argument to an invocation of the method referenced (your second example).

The Predicate accepts a parameter of whatever your generic type argument is. Your provided method reference must be able to do something with it.




回答3:


I want to add to @m3th0dman's answer.

You do have a way to define a Predicate<String> with a method reference of your Test class. The way to do it is by defining the isEmpty method to be static and have a String argument.

public class Test
{

  public static boolean isEmpty (String s)
  {
    return s.length() == 0;
  }

  public static void main (String[] args)
  {
      Predicate<String> p = Test::isEmpty;
      System.out.println(p.test("ff"));
      System.out.println(p.test(""));
  }

}



回答4:


When you used Predicate<String> then only the method reference of String is acceptable.
To give method reference of Test class you will need Predicate<Test>
Therefore the code should be :

Predicate<Test> p = Test::isEmpty;

instead of

Predicate<String> p = Test::isEmpty;

To make my point more clear consider the following code : -

public static void main(String args[]){
    Predicate<String> p;
    p = String::isEmpty;
    Predicate<Test> q;
    q = Test::isEmpty;
}

interface Predicate<T>{
    public boolean test(T t);
}

class Test{
    public boolean isEmpty(){
        return true;
    }
}


来源:https://stackoverflow.com/questions/25197066/java-8-functional-interface-assignment-context

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!