Reputation: 779
I've read the tutorial over and over again and I just don't understand the last part of approach 6.
Spcifically this part:
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
As a result, the following method invocation is the same as when you invoked printPersons in Approach 3: Specify Search Criteria Code in a Local Class to obtain members who are eligible for Selective Service:
printPersonsWithPredicate(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
So if p is a generic type how does it know it is a Person? I just don't get how it can distinguish itself as a person and later invoke the Person methods - getGender() and getAge(). Does p in this case refer to the class object of the surrounding class?
UPDATE:
Based on the provided answers, is this correct?
The following code:
interface CheckPerson {
boolean test(Person p);
}
class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
printPersons(roster, new CheckPersonEligibleForSelectiveService());
Is the same as:
import java.util.function // import the Predicate<T> interface
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
printPersonsWithPredicate(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
Because:
If I am wrong, please let me know, so that I can update my understanding. I really want to get this right.
Upvotes: 1
Views: 99
Reputation: 9705
In general: the parameter is simply inferred from the type signature of the relevant functional interface, including any additional limitations to the generic parameter.
In this case, we know that the second argument is a Predicate<T>
. Since Predicate<T>
is a functional interface with the method test(T t)
, we know that the type of p
is T
(and not Predicate<T>
).
And since T
is limited to Person
by the signature of printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester)
, the actual type of p
is Person
.
RE: UPDATE - the answer is both "yes" and "no" :).
"Yes", in general the sequence of type inference could be described this way.
"No", for the following reasons:
T
to Person
. The type person is simply the "common denominator" of what is allowed. For example: if the signature of the method would would contain:
Predicate<? extends JLabel>
- then the argument type would effectively be JLabel
.Predicate<?>
- then the argument type would effectively be Object
.Predicate<Person>
is legal, there is no need to actually create a new anonymous class that implements a functional interface. Indeed, in Java 8's current JDK, that's what happens. Here's an article explaining the details.Upvotes: 1
Reputation: 718798
Addressing the UPDATE questions only ...
1) Predicate interface is already defined in java.util.function
Correct
2) By declaring Predicate as Person in the method argument:
Incorrect.
You are actually declaring tester
as Predicate<Person>
. You are not declaring Predicate
at all here. Predicate
is a standard interface that it declared by the standard libraries.
2a) we are implicitly implementing Predicate with a type of Person
Correct ... sort of. You are explicitly implementing it. And the type of the lambda that is required here is explicitly specified ... by the method signature of printPersonsWithPredicate
. Specifically, because you declared the formal parameter tester
to have type Predicate<Person>
.
2b) when invoking the method with a generic type T (or lack thereof), the type is implicitly inferred by the method as Person
Not in this case.
Upvotes: 0
Reputation: 37845
The lambda expression is a definition for the body of Predicate<Person>#test
, which will take a Person
as a parameter. We know this because printPersonsWithPredicate
is declared to take a Predicate<Person>
, which the lambda is being passed as an argument for.
The syntax of a lambda is that the left hand side of the ->
is the parameter list for the method that is being defined:
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
However, the compiler is able to determine that the singular parameter p
of the lambda must be a Person so it can be omitted. Hence the shortened form p -> ...
.
An anonymous class near-equivalent to the lambda is shown in Approach #4.
Upvotes: 1