Page 1 of 1

About Question enthuware.ocpjp.v8.2.1789 :

Posted: Sat Oct 17, 2015 11:36 am
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

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

Posted: Sat Oct 17, 2015 9:49 pm
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.

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

Posted: Mon Oct 19, 2015 2:51 am
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.

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

Posted: Sat May 13, 2017 4:37 pm
by lenalena
The test code also doesn't compile because printNames is not static but is referenced from static void main (aka static context).

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

Posted: Sat May 13, 2017 8:24 pm
by admin
Did you try compiling it?

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

Posted: Mon May 15, 2017 2:11 pm
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

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

Posted: Mon May 15, 2017 11:48 pm
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.

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

Posted: Tue May 16, 2017 9:12 am
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!

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

Posted: Tue May 16, 2017 10:36 am
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.

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

Posted: Tue May 16, 2017 11:54 am
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).

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

Posted: Sat Jun 24, 2017 1:18 pm
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.

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

Posted: Sat Jun 24, 2017 8:57 pm
by admin
Yes, that could also be a valid reason.

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

Posted: Thu Jul 26, 2018 6:07 pm
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.