Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equality of instance of functional interface in java [duplicate]

I am not sure how I can be sure about equality/immutability of functional interface. I guess there might be no way to assure equality when I use this syntactic sugar in java 8, please let me know any hint if you have any.

I made a short code snippet for my question.

public interface Element {
    void doSomething(int a);
}

and I've tried to add instance of this interface in functional way

public class FunctionSet {

    public void doubleUp(int a) {
        System.out.println(a*2);
    }

    public void square(int a) {
        System.out.println(a*a);
    }

    public static void main(String[] args) {
        HashSet<Element> set = new HashSet<>();
        FunctionSet functionSet = new FunctionSet();

        set.add(functionSet::doubleUp);
        set.add(functionSet::square);

        System.out.println(set.add(functionSet::doubleUp));
    }

}

it prints true which means there were not any equality check and also I can't remove any instance from Set once I add it.

in case I use functional interface as an argument, Is there any way that I can compare those instance somehow?

will appreciate any help, thanks in advance!

like image 370
BinaryProbe Avatar asked May 11 '15 04:05

BinaryProbe


1 Answers

You can store your method reference into a variable:

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();
    FunctionSet functionSet = new FunctionSet();

    Element fn = functionSet::doubleUp;
    set.add(fn);
    set.add(functionSet::square);

    System.out.println(set.add(fn));
}

This way it returns false.

When you create the same labmda or method reference in different code locations, it's roughly the same as you would create a new anonymous class in both positions:

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();
    FunctionSet functionSet = new FunctionSet();

    set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.doubleUp(a);
        }
    });
    set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.square(a);
        }
    });

    System.out.println(set.add(new Element() {
        @Override
        public void doSomething(int a) {
            functionSet.doubleUp(a);
        }
    }));
}

So every time it's a different object, though it may look the same. For every encountered method reference separate anonymous class is created at the runtime:

Element e1 = functionSet::doubleUp;
Element e2 = functionSet::doubleUp;

System.out.println(e1.getClass());
System.out.println(e2.getClass());

The output will be like this:

class FunctionSet$$Lambda$1/918221580
class FunctionSet$$Lambda$2/1554547125

So practically it's two distinct objects of two distinct classes. It would be quite difficult to conclude that they do the same thing without comparing their bytecode. Also note that they both capture the functionSet variable, so it should also be ensured that it wasn't changed between two method references.

The only workaround I can think up is to declare all the method references as constants in your code and later reference them instead of using method references directly:

public static final Element FN_DOUBLE_UP = new FunctionSet()::doubleUp; 
public static final Element FN_SQUARE = new FunctionSet()::square; 

public static void main(String[] args) {
    HashSet<Element> set = new HashSet<>();

    set.add(FN_DOUBLE_UP);
    set.add(FN_SQUARE);

    System.out.println(set.add(FN_DOUBLE_UP));
}
like image 182
Tagir Valeev Avatar answered Oct 30 '22 13:10

Tagir Valeev