Please Explain Java 8 Method Reference to instance Method using class name

☆樱花仙子☆ 提交于 2019-12-28 06:33:12

问题


public interface MyFunc<T> {

    boolean func(T v1, T v2);

}
public class HighTemp {

    private int hTemp;

    HighTemp(){

    }
    public HighTemp(int ht) {
        this.hTemp = ht;
    }

    boolean sameTemp(HighTemp ht2){
         return hTemp == ht2.hTemp;
    }

     boolean lessThanTemp(HighTemp ht2){
        return hTemp < ht2.hTemp;
    }
}
class InstMethWithObjRef {

    static <T> int counter(T[] vals, MyFunc<T> f, T v){
        int count = 0;

        for (int i = 0; i < vals.length; i++) {
            if(f.func(vals[i], v)) count++;
        }
        return count;
    }
    public static void main(String[] args) {
        int count;
        //Create an array of HighTemp objects.
        HighTemp[] weekDayHighs = {new HighTemp(89), new HighTemp(82),
                                   new HighTemp(90), new HighTemp(89),
                                   new HighTemp(89), new HighTemp(91),
                                   new HighTemp(84), new HighTemp(83)};
        count = counter(weekDayHighs, HighTemp::lessThanTemp,new HighTemp(89));     
        System.out.println(count);          
    }
}

Please explain how

  1. boolean sameTemp() is compatible with func() in Functional interface.
  2. sameTemp() method got implemented on func() in Functional Interface.
  3. count = counter(weekDayHighs, HighTemp::sameTemp, new HighTemp(89)); is working

Please Explain All points separately.


回答1:


Equivalent lambda expression of HighTemp::lessThanTemp is

(highTemp1, highTemp2) -> {
     return highTemp1.lessThanTemp(highTemp2);
} 

This is one of the features of Java8 named Reference to an Instance Method of an Arbitrary Object of a Particular Type


Consider following example,

interface FIface<T> {
    int testMethod(T a, T b);
}

class Test2 {

    private String str;

    Test2(String str) {
        this.str = str;
    }

    int ok(Test2 test2) {
        System.out.println("Currnet String : "+ this.str);//Refer to t1
        System.out.println("Test String : "+test2.str);//Refer to t2
        return 0;
    }

}

public class Test {

    public static <T> int checkCall(T t1, T t2, FIface<T> fiFace) {
        //Here Test2 :: ok is equivalent to t1.ok(t2)
        return fiFace.testMethod(t1, t2);
    }

    public static void main(String[] args) {
        checkCall(new Test2("a"), new Test2("b"), Test2 :: ok);
    }

}

OUTPUT

Currnet String : a
Test String : b

Note here that Test2 :: ok is valid for the call even ok method is not static.

When you call the method checkCall for the functional interface you still have two arguments which are t1 and t2 and for that valid lambda expression can have parameters as (Test t1, Test t2) so your method Test2 :: ok here becomes valid for the call. Internally it works this way t1.ok(t2).

So, fiFace.testMethod(t1, t2); will will invoke method as t1.ok(t2)




回答2:


For starters I'm not a professional programmer. I too had a great difficulty in understanding the so called "Reference to an Instance Method of an Arbitrary Object of a Particular Type" I think this might be helpful for somebody who comes here from a google search.
I understood it a little bit with the help of lambda expressions.

In your code HighTemp::lessThanTemp as a Lambda expression would look like (x,y)->{x.lessThanTemp(y);} Replacing the method reference with this lambda expression would produce the same result. The above Lambda expression or the method reference both tell the interface method what to do.
When you use the method reference it tells the interface method to use the referred method from the given class, to carryout its function. Therefore if you convert HighTemp::lessThanTemp to English words it would sound something like "implement the lessThanTemp method form the class HighTemp as the implementation of the interface function". As you might've noticed in that case the return types and the argument types should be compatible. Otherwise you cannot implement an interface.

I would provide you another simple example code. More examples helps to understand this concept.

interface myint{
    int returnit(Test t ,int y);
}
class Test{
    int x=0;
    public Test(int x){
        this.x=x;
    }

    public int addNumbers(int y){
        return x+y;
    }
    public int subtractNumbers(int y){
        return x-y;
    }

}

public class myclass{
    private static void myMethod(Test t,myint inf,int y){
        int x=inf.returnit(t, y);
        System.out.println(x+"");
    }
    public static void main(String[] args){
        myMethod(new Test(4),Test::addNumbers,7);
        myMethod(new Test(4),Test::subtractNumbers,7);
    }
}


Output would be:

11
-3


This is the simplest way I could imagine it. See how return types and argument types gets matched using the above sentence pattern. Spend some time on it.




回答3:


This is the Interface

package learninglambdaexp;

@FunctionalInterface
public interface TempInterface {

    public boolean validTemp(Temperature temp);
}

This is the class

package learninglambdaexp;

public class Temperature {

    private int temp;

    public Temperature(int temp) {
        this.temp = temp;
    }

    public boolean isEvenTemp() {
        return temp % 2 == 0;
    }

    public boolean isOddTemp(){
    return !isEvenTemp();
    }
}

This is the Class with the Main Method

package learninglambdaexp;

import java.util.ArrayList;
import java.util.List;

public class AnotherMainClass {

    public static void main(String[] args) {

        List<Temperature> tempCollection = new ArrayList<>();
        tempCollection.add(new Temperature(100));
        tempCollection.add(new Temperature(20));
        tempCollection.add(new Temperature(30));
        tempCollection.add(new Temperature(40));
        tempCollection.add(new Temperature(50));
        tempCollection.add(new Temperature(60));
        tempCollection.add(new Temperature(70));
        int k1 = countVariation(tempCollection, Temperature::isEvenTemp);
        //int k2 = countVariation(Temperature::lowTemp);
        System.out.println(k1);
        // System.out.println(k2); 
    }

    private static int countVariation(List<Temperature> tempCollection, TempInterface ti) {
        int count = 0;
        for (Temperature eachTemp : tempCollection) {
            if (ti.validTemp(eachTemp)) { // (eachTemp) -> {return eachTemp.isEvenTemp();};
                count++;
            }
        }
        return count;
    }
}

With one argument its easier to understand




回答4:


Please, correct me if I am wrong, but the way I think about this type of method references (Reference to an Instance Method of an Arbitrary Object of a Particular Type) is that when we pass a method reference, in this case to the counter method, the instance of anonymous class which implements MyFunc interface is created. Then, inside this anonymous class, we override func method which is passed two parameters. And then inside the func method, lessThanTemp method is called like this:

v1.lessThanTemp(v2); 

So for me this concept looks something like this:

public class Demo {
    public static void main(String[] args) {
        AnonymousClass an = new AnonymousClass();
        System.out.println(an.apply(new SomeClass(3), 4));
    }
}
interface SomeInterface {
    int apply(SomeClass obj, int n);
}

class SomeClass {
    private int n;

    SomeClass(int n) {
        this.n = n;
    }

    int add(int n) {
        return this.n + n;
    }
}
class AnonymousClass implements SomeInterface {

    @Override
    public int apply(SomeClass o, int n) {
        return o.add(n);
    }
}


来源:https://stackoverflow.com/questions/32283833/please-explain-java-8-method-reference-to-instance-method-using-class-name

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