About Question enthuware.ocpjp.v8.2.1789 :

Help and support on OCA OCP Java Programmer Certification Questions
1Z0-808, 1Z0-809, 1Z0-815, 1Z0-816, 1Z0-817

Moderator: admin

Post Reply
mrmuiz
Posts: 49
Joined: Mon Jul 27, 2015 4:34 am
Contact:

About Question enthuware.ocpjp.v8.2.1789 :

Post by mrmuiz »

But the argument for forEach method requires a method that takes an argument. The forEach method basically invokes the passed method and gives that method an element of the list as an argument.
I think that, put in this way, it's a bit misleading. I'd say that
the argument for forEach method of a Collection<T> object requires
  1. a method that takes an argument of type T, or
  2. a method of the class T
Referring to the question, these are two examples, one for each case
  1. Code: Select all

    n.getList().forEach(System.out::print);
  2. Code: Select all

    n.getList().forEach(String::toString); //note that String::toString doesn't take any argument

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

That may not be true. Because in case of Collection<String>, T is String while System.out::print takes Object.
What you are saying describes what the argument for forEach should be. What the explanation is saying is what actually happens to the objects in the collection wrt to the method reference that is passed in as the argument in forEach.
HTH,
Paul.
If you like our products and services, please help us by posting your review here.

mrmuiz
Posts: 49
Joined: Mon Jul 27, 2015 4:34 am
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by mrmuiz »

Sure, my proposed answer is highly perfectible, I just wanted to give you a sense of what I meant.
The part I cited seems to refer to a general behaviour, for how it's written.
Maybe it's just me.

lenalena
Posts: 56
Joined: Tue Feb 21, 2017 4:24 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by lenalena »

The test code also doesn't compile because printNames is not static but is referenced from static void main (aka static context).

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

Did you try compiling it?
If you like our products and services, please help us by posting your review here.

lenalena
Posts: 56
Joined: Tue Feb 21, 2017 4:24 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by lenalena »

Yes. When compiled as is, the error given is:
error: incompatible types: invalid method reference
n.getList().forEach(Test::printNames);
^
method printNames in class Test cannot be applied to given types
required: no arguments
found: String
reason: actual and formal argument lists differ in length
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
However, when I add a String param to the method printNames, like this:

Code: Select all

   public void printNames(String name){
        System.out.println(getList());
    }
And try to compile, the compiler error is:
error: incompatible types: invalid method reference
n.getList().forEach(Test::printNames);
^
cannot find symbol
symbol: method printNames(String)
location: class Test
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
Then, if I add static to the printNames:

Code: Select all

public static void printNames(String name){
        //System.out.println(getList());  
    }
It compiles fine, without any issues. (Note, commented out method body to prevent non-static call to getList()

Compiled using command prompt, no IDE. javac version 1.8.0_121

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

No, that is an incorrect interpretation. It is possible to reference a non-static method from a static method as long as you are invoking the non-static method on a valid object reference.
In this case, when the method reference Names::printNames will invoked the printNames method on each object returned by forEach.

It will not compile just by changing the printNames method to static. As you posted above, you had to comment out the call to System.out.println(getList()); as well.

HTH,
Paul.
If you like our products and services, please help us by posting your review here.

lenalena
Posts: 56
Joined: Tue Feb 21, 2017 4:24 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by lenalena »

Paul, I'm sorry, I think I'm missing a big concept here, but I still don't get it. I think the fact that I commented out getList() call from printNames just proves my point, not disproves it.

So I left printNames(String s) completely empty. If I remove static keyword - it doesn't compile (with the error in second example above). If I add static keyword - it does. How can it be said, then, that compilation error is NOT due to non-static reference from a static context?

The compiler doesn't even see the method unless it is made static.

The necessity to comment out getList () was just a waterfall effect - when getNames became static - then it in turn started making a non-static reference to getList. I added System.out.println(getList()) back to static printNames and tried to recompile, and got the following error:
error: non-static method getList() cannot be referenced from a static context
System.out.println(getList());
^
1 error
Now there is a more direct message.

I agree with your general statement, that
It is possible to reference a non-static method from a static method as long as you are invoking the non-static method on a valid object reference.
After all, the static main method is successfully calling getLest() on the non-static TestClass reference it just created. However, I do not think that forEach does something similar, in this case anyway. If it had, then indeed non-static printNames could have been invoked on an element. And the presence or absence of any method body in printNames would have been irrelevant - after all, it would just be a method call. But the compiler is not allowing a non-static call to printNames, so method reference to a non-static method is not allowed in a static context, even though it will be applied to a valid non-static object reference returned by forEach.

I hope that wasn't too confusing. Thank you for reading and working through this with me!

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

The original method was:
public void printNames(){
System.out.println(getList());
}
and you changed it to:
public static void printNames(String x){
}

In my view, you have changed everything about the method (including its signature as well as business logic) and not just made it static from non-static. Yes, it compiles but it the not the same method at all. If static/non-static were the real issue, you would not have had to make other changes.

The simple and most straight forward reason is what is given in the explanation, which is -
n.getList().forEach(Names::printNames); should be changed to simply: n.printNames();

It is not the method printNames that has a problem. It is the statement in which it is referenced that has a problem.

HTH,
Paul.
If you like our products and services, please help us by posting your review here.

lenalena
Posts: 56
Joined: Tue Feb 21, 2017 4:24 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by lenalena »

My reason for not compiling is the secondary reason. Of course, the reason given in the solution - the fact that printNames() takes to arguments - is the primary reason. However, IF that were the only reason, then adding a String parameter to printNames would have fixed the problem. However, even when printNames gets a parameter so that the signature would fit the required consumer signature - then it STILL doesn't compile - because of the (secondary) static issue. (IMHO).

lucastomasini
Posts: 3
Joined: Tue Jun 20, 2017 3:10 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by lucastomasini »

If I add any argument to the printNames method, for example:

public void printNames(String hola){
System.out.println(getList());
}

It still doesn't compile because the compiler can't infer from the context what instance of printNames to use (printNames is not a static method). I got the error:

error: incompatible types: invalid method reference
n.getList().forEach(Names::printNames);
cannot find symbol
symbol: method printNames(String)
location: class Names

But if I change the last sentence to: n.getList().forEach(n::printNames);

then it does compile.

So the reason why it doesn't compile is not only that the forEach method requires a method that takes an argument. Correct me if I am wrong.

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

Yes, that could also be a valid reason.
If you like our products and services, please help us by posting your review here.

__JJ__
Posts: 125
Joined: Thu Jul 05, 2018 6:44 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by __JJ__ »

I think this is quite an important concept.
We have learnt from other questions that you can supply a INSTANCE method reference of the form X::foo, where foo takes no parameters, as an argument to a method (call it bar) that expects a method that takes a parameter, and the compiler is smart enough to recognise that an instance of X will be the required parameter, but it only works if what's going into bar is the same type as X.
That sounds a bit like gobbledegook but I can see now why this works:

Code: Select all

       n.getList().forEach(String::toString);
but this doesn't

Code: Select all

      n.getList().forEach(Names::printNames);
ie it's because getList supplies a String; so you can either supply any method reference X::xyz where xyz takes a String or you can supply a reference to a no-arg instance method of String in which String becomes the required argument corresponding to apply(T t).
Of course you could supply any one-arg static method that takes a String also.

Javier
Posts: 66
Joined: Mon Feb 20, 2017 12:31 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by Javier »

I think in this last paragraph there is a little mistake:
"String becomes the required argument corresponding to apply(T t)."
Instead of apply (T t) should be accept (T t), because the interface to implement in forEach is Consumer.

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

Right.
If you like our products and services, please help us by posting your review here.

jme_chg
Posts: 29
Joined: Sun Feb 07, 2021 6:30 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by jme_chg »

When I change
printName(), getList(), list all to static rather than instance,

I thought that

Code: Select all

forEach(Names::printNames)
would now work as it gets interpreted as

Code: Select all

forEach(string -> Names.printNames())
but it does not
is there an explanation for this? :/

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

What error message do you get when you compile it?
If you like our products and services, please help us by posting your review here.

jme_chg
Posts: 29
Joined: Sun Feb 07, 2021 6:30 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by jme_chg »

Code:

(using https://www.onlinegdb.com/online_java_compiler - so had to rename class for Names to Main)

Code: Select all

import java.util.*;
import java.util.stream.*;
public class Main{     
    private static List<String> list;     
    public static List<String> getList() {         
        return list;     
    }     
    public void setList(List<String> list) {         
        this.list = list;     
    }     
    public static void printNames(){         
        System.out.println(getList());
    }      
    public static void main(String[] args) {        
        List<String> list = Arrays.asList(         
            "Bob Hope",         
            "Bob Dole",         
            "Bob Brown"        
        );         
        Main n = new Main();        
        n.setList(list.stream().collect(Collectors.toList()));        
        n.getList().forEach(Main::printNames);    
    }      
}  
Output:

Main.java:22: error: incompatible types: invalid method reference
n.getList().forEach(Main::printNames);
^
method printNames in class Main cannot be applied to given types
required: no arguments
found: String
reason: actual and formal argument lists differ in length
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error


But if I replace:

Code: Select all

forEach(Names::printNames)
with:

Code: Select all

forEach(string -> Names.printNames())
it works fine...
Aren't these the same thing?? :S
Any ideas?

admin
Site Admin
Posts: 10036
Joined: Fri Sep 10, 2010 9:26 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by admin »

No, they are not the same!
forEach(Names::printNames) implies this:

forEach( new Consumer(){
public void accept(String s){
Names.printNames(s); //this will not compile because there is no such method in Names
}
} );

forEach(string -> Names.printNames()) implies this:

forEach( new Consumer(){
public void accept(String string){
Names.printNames(); //this is ok. the string variable is ignored/not used
}
} );

You will need to go through some book or tutorial that explains how a method reference is used by the compiler to generate an implementation of the functional interface and how a lambda expression is used. Both are different.
If you like our products and services, please help us by posting your review here.

jme_chg
Posts: 29
Joined: Sun Feb 07, 2021 6:30 pm
Contact:

Re: About Question enthuware.ocpjp.v8.2.1789 :

Post by jme_chg »

I think I understand...
I played around with some code and it seems like:

When converting method reference to lambda,
All the parameters on the LHS of the lambda need to also be used in the RHS...

Is this correct?

Post Reply

Who is online

Users browsing this forum: Google [Bot] and 37 guests