Win a copy of Five Lines of Code this week in the OO, Patterns, UML and Refactoring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Ron McLeod
  • Jeanne Boyarsky
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Liutauras Vilda
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • fred rosenberger
  • salvin francis
Bartenders:
  • Piet Souris
  • Frits Walraven
  • Carey Brown

Question about lambdas

 
Ranch Hand
Posts: 33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am learning lamdas and I sometimes wonder how it works when very limited information is given to Java. For instance, the removeIf() method has a signature like this: boolean removeIf(Predicate<? super E> filter) . With the lower-bounded wildcard generics in play, I was wondering how Java knows what real parameter type is used in runtime. For instance, with this code:

In this line ducks.removeIf(d->d.isDuck());  ,how does Java know if it should use a Predicate<Duck> or a Predicate<Animal> as we never specify it? Thanks in advance!
 
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The type of d in the lambda expression, d -> d.isDuck(), is inferred.  When you declare the list as List<Duck> and the declaration of the method is removeIf(Predicate<? super E> filter), then the compiler is already given the information it knows to interpret the generic as removeIf(Predicate<? super Duck> filter), since E == Duck.  The lambda then is inferred to be equivalent to { Duck d -> return d.isDuck(); }
 
Edwardd Lee
Ranch Hand
Posts: 33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
But the Predicate can take any class that is super to Duck right? Since E is Duck, how can Java be confident we are  using Predicate<Duck> instead of Predicate<Animal>? It could very well be {Animal d -> return d.isDuck(); } we are inferring.
 
Marshal
Posts: 25674
69
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The compiler infers that the "d" parameter is coming from a List<Duck>, and therefore that it is a Duck.
 
Bartender
Posts: 3998
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It is also a matter of overriding. You can use a List<Animal> instead of a List<Duck> and then removeIf( d -> d.isDuck()) still removes only the ducks.
 
Junilu Lacar
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Edwardd Lee wrote:But the Predicate can take any class that is super to Duck right? Since E is Duck, how can Java be confident we are  using Predicate<Duck> instead of Predicate<Animal>? It could very well be {Animal d -> return d.isDuck(); } we are inferring.


You've confused yourself because your example doesn't make much sense.  If you declare a List<Duck> then the only type of objects the list will take are Duck objects. You'll never have Animal objects in there so your concern is irrelevant.

Here's a better example:

This program prints the following:

2
Chicken
Pig
 
Edwardd Lee
Ranch Hand
Posts: 33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You guys are absolutely right, there are serious issues with my example code. I've done some experiments and find this to be a better example:

The output is 1 when I specify in this line ducks.removeIf((Animal d)->d.isDuck()); that I want Java to use Animal instead of Duck.
However, if I add this line to the main method: The line wouldn't compile because the method signature of replaceAll is void replaceAll (UnaryOperator<E> o) , therefore I can only use Duck but not one of its superclasses like Animal to be the lamda parameter.
That's really interesting, and I hope this is the right understanding?
 
Piet Souris
Bartender
Posts: 3998
156
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, since Duck is an Animal, there are no problems. For instance this method works:

and so this works as well:
 
Junilu Lacar
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Edwardd Lee wrote:... I've done some experiments and find this to be a better example:


That example still makes little sense.

Your list is still declared as a List<Duck> so the lambda expression on line 6 makes no sense.

When Why would you declare the lambda parameter to have a type of Animal when it's clear that the object it will receive will always be a Duck object?  

Also, why use the letter "d" as the name for a reference to an Animal? That kind of shows that you've really got Ducks on your mind, not Animals.  

This new sample program of yours will always delete all the Duck objects that were added to it prior to the call to removeIf(). The result of line 6 is always to clear out the list. I don't see how you think that makes sense at all since the result will always be the same no matter how many objects you add to the list prior to the call on line 6.
 
Edwardd Lee
Ranch Hand
Posts: 33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:
When Why would you declare the lambda parameter to have a type of Animal when it's clear that the object it will receive will always be a Duck object?


Because I'm experimenting how Java knows which Predicate generics is used in runtime if the Predicate<? super E> that removeIf takes can be a Predicate of any classes that is super to Duck. I find that if I leave out the object type in lambda parameter, i.e. d -> d.isDuck();, Java will assume d to be Duck thus uses Predicate<Duck> . Whereas if I specify the parameter type, such as (Animal d) -> d.isDuck(); Java will then know that I want to use Predicate<Animal> instead of Predicate<Duck>.

Junilu Lacar wrote:
Also, why use the letter "d" as the name for a reference to an Animal? That kind of shows that you've really got Ducks on your mind, not Animals.  


Yeah I agree that my second example doesn't make any sense logically in the real world. My second example is a modification of my first example because as Piet Souris rightfully pointed out that my original example has a method overriding issue, thus in my second example I make the isDuck( ) method in the parent class Animal private.

Junilu Lacar wrote:
This new sample program of yours will always delete all the Duck objects that were added to it prior to the call to removeIf(). The result of line 6 is always to clear out the list. I don't see how you think that makes sense at all since the result will always be the same no matter how many objects you add to the list prior to the call on line 6.


Can you please explain why this is the case? Because when I tested the second code I posted, the output is 1, indicating that no Duck is removed before or after I call removeIf( ).
 
Junilu Lacar
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Edwardd Lee wrote:Can you please explain why this is the case? Because when I tested the second code I posted, the output is 1, indicating that no Duck is removed before or after I call removeIf( ).


In the second version of your sample, you declared the isDuck() method in the Animal class as private, therefore, it could not participate in polymorphism. If you change that method and make it public, the program will clear out the list like I said it would.

Output:

Before removeIf(): 20
After removeIf(): 0

You should always use the @Override annotation when you think you are overriding a method. The compiler will complain if you're not actually overriding anything. It would have done that if you hadn't left out the @Override for the isDuck() method in your Duck class and that would have clued you in that something was amiss.
 
Junilu Lacar
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Edwardd Lee wrote:...I'm experimenting how Java knows which Predicate generics is used in runtime if the Predicate<? super E> that removeIf takes can be a Predicate of any classes that is super to Duck. I find that if I leave out the object type in lambda parameter, i.e. d -> d.isDuck();, Java will assume d to be Duck thus uses Predicate<Duck> . Whereas if I specify the parameter type, such as (Animal d) -> d.isDuck(); Java will then know that I want to use Predicate<Animal> instead of Predicate<Duck>.


I don't think your analysis is correct. Generics are for compile time verification only and when the compiler is done doing that, generic types are replaced through the process of type erasure. For a lower bounded wildcard generic type like in the Predicate<? super E>, I think the generic type is replaced by Object. You'll have to read more on type erasure if you want to know more; I'm not well-versed in the ins and outs and all the nuances of that process.
 
Piet Souris
Bartender
Posts: 3998
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
hi Edwardd,

I just noticed what you wrote (in one of your replies), about making the 'isDuck' method privvate in the Animal class:

Edwardd Lee wrote:You guys are absolutely right, there are serious issues with my example code. I've done some experiments and find this to be a better example:

The output is 1 when I specify in this line ducks.removeIf((Animal d)->d.isDuck()); that I want Java to use Animal instead of Duck.
However, if I add this line to the main method: The line wouldn't compile because the method signature of replaceAll is void replaceAll (UnaryOperator<E> o) , therefore I can only use Duck but not one of its superclasses like Animal to be the lamda parameter.
That's really interesting, and I hope this is the right understanding?

You are right, you can now only use a List<Duck>, since a List<Animal> in combination with 'remeoveIf(d -> d.isDuck()) doesn't compile indeed.

Yes, go on experimenting, it is the best waty to discover things.
 
Junilu Lacar
Sheriff
Posts: 15760
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Maybe it's just me but I fail to see what the practical applications are for this line of inquiry.  Going through all those gyrations with forcing Java to use Animal instead of Duck is fine if you're only interested in understanding how the underlying mechanisms work. However, I have to keep pulling my focus back out to the conceptual level, hence my constantly coming back to the question, "Why would you want to force your desire in treating the list elements as Animal objects when the List is clearly declared as one that will contain only Duck objects."  It just makes no logical sense to me.

In college, we went on plant tours at the Coca-Cola bottling plant and the Pepsi bottling plant that were in our area, in that order. At the Pepsi plant, we saw the same kind of setup we saw at the Coke plant, where a line of inspectors stood spread out alongside a fast-moving conveyor belt, staring intently at the bottles of product whizzing by them. One of our classmates asked, "What are these inspectors looking for anyway?" and another of our classmates quickly answered, "He's looking to make sure there aren't any Coke bottles that sneak in." That's kind of how I view this example and exercise.
 
Don't get me started about those stupid light bulbs.
    Bookmark Topic Watch Topic
  • New Topic