• 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 Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Rob Spoor
  • Tim Cooke
  • Junilu Lacar
Sheriffs:
  • Henry Wong
  • Liutauras Vilda
  • Jeanne Boyarsky
Saloon Keepers:
  • Jesse Silverman
  • Tim Holloway
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Mikalai Zaikin
  • Piet Souris

Consumer Function

 
Ranch Hand
Posts: 39
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Guys

I am aware that consumer takes a parameter and does not return anything !!

But the below two programs are quite confusing for me Can someone please help me understand the same.

This is the first program!
According to the explanation : printNames does not take any argument But the argument for forEach method requires a method that takes an argument and hence this doesn't compile




The below is the second program. Even in this program inside forEach a method that doesnt take any parameter is used but this doent throw any compiler error. Can someone please explain the difference.

I am not sure what i am missing here!!.

What will the following code print when compiled and run?

List<Student> slist = Arrays.asList(new Student("S1", 40), new Student("S2", 35), new Student("S3", 30));
Consumer<Student> increaseMarks = s->s.addMarks(10);
slist.forEach(increaseMarks);
slist.stream().forEach(Student::debug);

Thanks !!!



 
Saloon Keeper
Posts: 1277
38
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Priyanka:

I think you are not getting confused about functional interfaces and lambdas (you might be but I think you are good there).

I think the tricky part that has you unsure is the different types of Method References, and exactly how they correspond to lambdas.

So there are four types of Method References, right?  Let's go thru them!

1. Constructor Reference:
Syntax: ClassName::new
Meaning: this will call the constructor for ClassName() and return a new instance of that class, even tho Constructor doesn't declare any return type value, I guess why we call it ClassName::new instead of ClassName::ClassName, maybe?  The interesting part here is that it might be calling the NoArgs constructor if we assign it to a Supplier<ClassName> but could also be calling some other Constructor Overload if we assign it to a functional interface that passes parameters.  It is definitely interesting and powerful and tricky but not what you are confused about at the moment, but do look into the different ways to call constructors, interesting, powerful, tricky stuff.

2. Static Method Reference:
Syntax: ClassName::staticMethodName
Meaning: this will call staticMethodName() using whatever parameters get passed in at the invocation (I am trying to stop saying "call site" because people don't like that phrase).  Kind of the simplest, most straightforward type of Method Reference, because what you see at the call site invocation is what you get.  You are unlikely to get confused by these, but we listed it, on to the next one.

3. Instance Method Reference Bound to a Particular Instance:
Syntax referenceToSomeObjectOfSomeActualType::instanceMethodName
Meaning: this will call instanceMethodName() when invoked, and since it was an instance method, it will call it specifically on the object referred to by referenceToSomeObjectOfSomeActualType at the moment that we assign the method reference to the functional interface reference variable.  I found this confusing, because it is legal for referenceToSomeObjectOfSomeActualType to change afterwards, but we are already married to the object instance at the moment it had been assigned before.  The Object instance that we get called on itself may change in-between this assignment and our eventual invocation, and we will see those changes, but the reference variable before the :: is done with its job as soon as we assign this method reference to the functional interface reference variable (on the left side of the = that the method reference gets assigned to)...what is additionally weird about this is that the object instance that will be called on has already been determined.  Any parameters passed at the place and time of invocation go right into the parameters of the instance method, just like in a static method reference.  It already knows which instance the method is being called on.

4. Instance Method Reference Called on an Instance to Be Named Only Later At Invocation Time:
Syntax: ClassName::instanceMethodName
Meaning: (Whew!!  Last of the four types, we are almost done!)  This will call instanceMethodName() when invoked, using the first parameter being passed at invocation time as the instance of the object on which to call the instance method.  Any parameters after the first one at the invocation site will be used as the parameters to that instance method invocation on the instance specified by the first parameter there...This one is weird because when we assign the reference it *looks* like a static method reference, see how it says ClassName:: ?  But later on it WORKS differently, because the first parameter where it is called (maybe the only parameter!) determines which object instance has a method called, and only parameters after the first will be considered part of the parameters to the method call.  This was weird to me at first, but if we work with all four types for a while, it will make sense, or at least stop feeling weird.  Still, the fact that two things that look similar in syntax behave differently, and that two things that look DIFFERENT in syntax behave similarly (but not the same) I found this one of the trickier parts of all functional programming, to be honest.

Anyway, if you get all that you are totally down and cleared on all knowledge about method references, even if you phrase it in your own words somewhat differently.  You need to know those four types of method reference, how they look where they are assigned and where we invoke them, and what is going on with that.

Now to your actual question/confusion, and perhaps you already see this right now, if not I hope you will agree the confusion is about having those four kinds of method references clear in your own mind when looking at this.

public void debug(){
       System.out.println(name+":"+marks);
   }

Consumer<Student> increaseMarks = s->s.addMarks(10);
slist.forEach(increaseMarks);
slist.stream().forEach(Student::debug);


we see that debug() takes NOTHING and returns NOTHING, which doesn't seem to be a Consumer<> because it doesn't appear to be taking anything...confusing...it only makes sense when we see what functional interface it gets assigned to, let's keep going.

addMarks() makes sense that it is a Consumer because we see it is taking a Student and calling .addMarks( 10 ) on that Student reference.  So easy to see in the lambda definition.
When does it get called?  In the forEach( increaseMarks ) we see that our lambda gets called with each Student reference in turn, has a side effect and returns nothing.  That is clear to all of us.

Then we get to the Stream of Students, and know that .forEach() takes a Consumer, here a Consumer<Student>.
How is it that debug() which looks like it takes no parameters is actually a Consumer of something??

If you don't see it already you will later, and at that moment whenever it is, you will say "This is a method reference of the Fourth Kind.  So, when we invoke it inside the .forEach() the parameter being passed to it is the Student reference that we want to call .debug() on.  If there were more parameters at the invocation they would have been passed to debug() as parameters, but there are not and debug() doesn't take them.  So the confusing line of:
slist.stream().forEach(Student::debug);

is like a lambda of:
slist.stream().forEach(s -> s.debug() );

which we can see is a lambda that takes something (one Student) and returns NOTHING, which is nothing but a Consumer<Student>

To totally completely master this part of Java, you need to get used to taking each kind of method reference and figuring out what the equivalent lambda would look like.
It is tricky for reasons that I described before and even trickier when the method reference is used directly like we do here with Student::debug and we never get to see it assigned to a reference variable of some functional interface type that is explicitly shown.  We are trading off compactness (which is good) for explicit clarity (what is meant is well-defined and known, but not really shown in front of our eyes, unless we remember the four types of method references well and can re-map them in our minds to the corresponding lambdas).  Until that point we can recommend if we are writing code for ourselves or work "Don't use method references then, just write lambdas instead" but that doesn't really help those of us who are taking the 819 at all.  For us, I would instead recommend "Get familiar with the (almost) equivalence of the different kinds of method references with the lambda expressions they are almost the same as.  It will take a while, because it is a bit weird and there is stuff going on that isn't shown explicitly each time but gets pieced together by what we understand about the kinds of method references.  Once you get it, they allow you to write VERY short code that runs great and can be read very quickly by those who are no longer confused about the four kinds of method references because they have gotten used to them"

This was way more than you asked for, but I have been studying this pretty recently, I and concluded until I was solid on this I would be fearful of and uncomfortable with method references, because what is going on isn't all shown right in front of your eyes for you to see like with the lambda expressions.
 
I like you because you always keep good, crunchy cereal in your pantry. This tiny ad agrees:
Thread Boost feature
https://coderanch.com/t/674455/Thread-Boost-feature
reply
    Bookmark Topic Watch Topic
  • New Topic