1

I am currently implementing a way to match a Predicate to a text string

To do that matching I'm filling a Map of < Predicate, String> and then I can retrieve the corresponding String via the predicate.

The problem is that when using a parameterized predicate like in the code below the same predicate match for different parameters (valueToTest not used in the hashing function)

public static Predicate<MyObject> predicateCondition(String valueToTest) {
    return myObject -> myObject.value.equals(valueToTest);
}

So currently I need to make one predicate per parameter I would want to test ie :

public static Predicate<MyObject> predicateConditionValue1() {
    return myObject -> myObject.value.equals("value1");
}

public static Predicate<MyObject> predicateConditionValue2() {
    return myObject -> myObject.value.equals("value2");
}

Would there be another way to avoid duplicating the predicate?

T. Pietri
  • 13
  • 3

3 Answers3

1

tl;dr:

First at all you should to know that Predicate is not class but it is interface. And lambda's are almost anonymous classes (with few differences, which are not important in scope of your question). So all your three functions which you've show in your question, returns objects which are instances of three different classes (of course, all of them implement same interface, that's why you can use all of them as keys in Map). But map (which is actually interface too, so you need to understand how works exact map implementation, which you're using in your code) usually use equals(), hashCode() and sometimes maybe compareTo() of your key-class. And because you're using anonimous classes (strictly: 'almost anonymous classes'), it uses equals() and hashCode() from class Object. Where hashCode() not depends on object methods and data, but created by JRE, and equals() compares that hash-codes.

Briefly:

Don't use anonimous (declared as lambda) predicates as keys in your map. Create your own class which implements Predicate interface and have your own implementation of hashCode(), equals() and test(). If you still want use lambda's, use these anonimous predicates as field of you own key-class.

Generally:

To avoid such problems just consider Lambda's in java as a syntax sugar (lambda's are not just syntax sugar, but almost syntax sugar). When you're writing code:

Predicate<String> aPredicate= s->"asd".equals(s);

consider it as special form of code below:

Predicate<String> aPredicate= new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return "asd".equals(s);
            }
        };
Ivan Zelenskyy
  • 659
  • 9
  • 26
  • The problem isn't about using the parameterized predicate, currently I'm filling a map of , String> and need to retreive the corresponding string for each predicate while still deferentiating on the valueToTest, ie I want to get 2 differents result for map.get(predicateCondition("a") and map.get(predicateCondition("b")) – T. Pietri Nov 28 '18 at 10:32
  • do you have a Map, where predicates are keys and strings are values? Or you've your own implementation of Map? – Ivan Zelenskyy Nov 28 '18 at 10:39
  • I have a HashMap where predicates are key and strings values currently – T. Pietri Nov 28 '18 at 10:52
  • 1
    In other words, predicates are already non-equal, as every Object in Java is not equal to another by default. – Basilevs Nov 28 '18 at 11:10
  • Thank you, I will create a class for each type of predicate – T. Pietri Nov 28 '18 at 11:34
  • "lambda's are anonymous classes" - No, they aren't. [Oracle Docs](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html): "Note that a lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name." More: [Java8 Lambdas vs Anonymous classes](https://stackoverflow.com/questions/22637900/java8-lambdas-vs-anonymous-classes) – LuCio Nov 28 '18 at 11:47
  • "hashCode() is phisical address of your object" - [not necessarily](https://stackoverflow.com/a/4933258/2838289) – LuCio Nov 28 '18 at 11:56
  • About ["lambda's are not just syntax sugar, but almost syntax sugar"](https://stackoverflow.com/questions/15221659/java-8-lambda-expression-and-first-class-values) – LuCio Nov 28 '18 at 12:40
0

As alternative you can use BiFunction instead:

BiFunction<MyObject, String, Boolean> isEquslTo = (myObject, expected) -> Objects.equals(myObject.value, expected);

But I like approaches with two Predicate more.

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
0

To avoid duplicating predicates, you should rather use BiPredicate<T,U> which accepts one more parameter and produces a Boolean as well.

BiPredicate<MyObject, String> biPredicate = (obj, string) -> myObject.value.equals(string)

In a method:

public static BiPredicate<MyObject, String> predicateConditionValue() {
     return (myObject, string) -> myObject.value.equals(string);
}
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183