About Question enthuware.ocpjp.v8.2.1871 :

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

Moderator: admin

Post Reply
Martyjee
Posts: 32
Joined: Tue Oct 06, 2015 9:06 am
Contact:

About Question enthuware.ocpjp.v8.2.1871 :

Post by Martyjee »

The explanation contains the following:
Similarly, Tiger::eat is invalid because eat is an instance method of Tiger. You cannot invoke it on Tiger class
In fact, in general, you can! The method can be referenced from any context, because its signature is public. In this example you can "invoke" t::eat or Tiger::eat. The difference is that the parameters of their respective lambda expressions are different! In the case of t::eat, the parameter list is equivalent to (List<String> p1). In the case of Tiger::eat, the parameter list is equivalent to (Tiger p1, List<String> p2). So the real reason that option 5 is incorrect is that the equivalent lambda expression of the method reference does not have required parameters.

Kind regards,

Martijn

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

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

Post by admin »

Not sure I agree with what you are saying. As per the code that is given, if you use Tiger::eat, you are implying that you want to call eat method on Tiger class (as opposed to Tiger instance), which is not possible because eat is an instance method. There is no context here that provides you a Tiger instance.

Could you please show with code what you mean?

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

Martyjee
Posts: 32
Joined: Tue Oct 06, 2015 9:06 am
Contact:

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

Post by Martyjee »

Hi Paul,
As per the code that is given, if you use Tiger::eat, you are implying that you want to call eat method on Tiger class (as opposed to Tiger instance)
No, that is not true, you are implying that you want to use the code that is defined in the eat method of the type Tiger! The Oracle docs define 4 flavours of method references, and this flavour (the most confusing one) is what they call "Reference to an Instance Method of an Arbitrary Object of a Particular Type". Remember that we are dealing with lambda expressions and method references here, not actual method calls!
There is no context here that provides you a Tiger instance.
You don't need a Tiger instance, and the eat method does not have to be static to reference it as Tiger::eat.
Could you please show with code what you mean?
I will give a code example that I came up with (assume appropriate import statements):

Code: Select all

public class Example {
    public static void main(String[] args) {
        Mother mother = new Mother();
        Child child = new Child();
        mother.giveOrder(child::eat);   //1 Reference to an instance method of a particular object
        mother.giveOrder(Knife::slice); //2 Reference to an instance method of an arbitrary object of a particular type
        mother.giveOrder(Knife::stab);  //3 Reference to a static method
    }
}

@FunctionalInterface
interface Eater {
    void eat(Knife k, List<String> eatableThings);
}

class Child implements Eater {
    @Override
    public void eat(Knife k, List<String> eatableThings) {
        eatableThings.stream().forEach(s -> System.out.println(s + " was eaten..."));
    }
}

class Knife {   
    final String length = "long";
    
    public void slice(List<String> eatableThings) {
        eatableThings.stream().forEach(s -> System.out.println(s + " was sliced with a " + length + " knife"));
    }
    
    public static void stab(Knife knife, List<String> eatableThings) {
        eatableThings.stream().forEach(s -> System.out.println(s + " was stabbed..."));
    }
}

class Mother {
    void giveOrder(Eater eater) {
        eater.eat(new Knife(), Arrays.asList("Meatball", "Pasta", "Carrot")); //4
    }
}
As you can see at line //2 there is no instance needed to reference Knife::slice
It compiles fine! The context is created at line //4 where the actual Knife object is first needed!
It looks like the "slice" method does not have the required parameters to be treated as an Eater, but actually it does, because the compiler inserts some arbitrary object reference to the parameter list as its first parameter! The program will even cause a compilation error at //2 if we rename the method "stab" to "slice", because their parameter lists are effectively the same when used as a lambda expression!!

HTH,

Martijn

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

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

Post by mrmuiz »

Martyjee, I agree with you when you say that

Code: Select all

process(fnames, Tiger::eat);
can be interpreted in two ways
  • the intention to call a static method of Tiger class, or
  • the intention to call an instance method on a given instance of Tiger
But in the second case you obviously need a Tiger instance: it should be provided by process method:

Code: Select all

c.eat(new Tiger(),names);
but then Carnivore method should be

Code: Select all

int eat(Tiger t, List<String> foods);
and Tiger should not implement Carnivore to compile...
I mean, it should be another example.
You don't need a Tiger instance, and the eat method does not have to be static to reference it as Tiger::eat.
This is not correct, you DO need an instance. In your example //4 is providing it. I mean, you don't need an instance in that very line where method reference is defined, but you definitely need one.

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

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

Post by admin »

As per https://docs.oracle.com/javase/tutorial ... ences.html, the syntax for "Reference to a static method" and for "Reference to an instance method of an arbitrary object of a particular type" is same i.e. ContainingClass::methodName

Which one do you mean can only be inferred from the context in which you are using this syntax. So while I agree with you that the explanation should be improved to make this point clear, in this case, Tiger:eat would imply that you want to refer to a static method because there is no Tiger instance available in the context on which the eat method can be invoked.

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

Martyjee
Posts: 32
Joined: Tue Oct 06, 2015 9:06 am
Contact:

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

Post by Martyjee »

Which one do you mean can only be inferred from the context in which you are using this syntax
It is in fact inferred at compile time! That is the point I am trying to make. You don't need a instantiation of a Tiger object to be able to write "Tiger::eat". So one last example:

Code: Select all

class Tiger {
public eat(Mouse m){}//1
public static void eat(Tiger t, Mouse m){}//2
}
In the above example, Tiger::eat could refer to both methods and compilation fails. Their parameter lists are effectively the same (when used as a lambda expression). Of course, what mrmuiz is also saying, to actually use method number //1, you need some arbitrary instance of Tiger, because the code in the method depends on an instance (in my example, Knife::slice needs the field "length").

So the explanation in the question should be changed to something like:

"Similarly, Tiger::eat is invalid and will not compile because the instance method eat does not satisfy the required parameter list (when used as a lambda expression) needed by Carnivore". Tiger::eat has (Tiger t, List<String> l), it needs (List<String> l)".

HTH,

Martijn

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

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

Post by admin »

Hi Martijn,
Thanks a lot for taking time in working through this with me. I really appreciate that.

Code: Select all

class Tiger {
public void eat(Mouse m){}//1
public static void eat(Tiger t, Mouse m){}//2
}
BiConsumer<Tiger, Mouse> c = Tiger::eat; //won't compile
This fails to compile because of ambiguity. Inside the BiConsumer, it could call Tiger.eat(t, m) or it could also call t.eat(m). Both are equally applicable.

So yes, I agree with you that Tiger::eat doesn't refer to the static method just because you are using Tiger:: instead of t::.

I think I am closer to understanding your point now but consider the following code. The difference between your code and this code is that I have separated out Tiger and Animal classes to remove ambiguity.

Code: Select all

import java.util.function.BiConsumer;
import java.util.function.Consumer;


class Mouse {    }
class Tiger{   }
class Animal {
    public void eat(Mouse m){    }
    public static void eat(Tiger t, Mouse m){    }
}

public class TestClass {
    public static void main(String[] args) {
        Mouse m = new Mouse();
        Tiger t = new Tiger();
        Animal a = new Animal();
        Consumer<Mouse> c1 = a::eat; //1. This compiles fine
        Consumer<Mouse> c2 = Animal::eat; //2. This doesn't compile. Error message is: "non-static method eat(Mouse) cannot be referenced from a static context"
        BiConsumer<Tiger, Mouse> c3 = Animal::eat; //3 This compiles fine
    }
}
This tells me that Animal::eat could refer to static or instance method. However, the message in line //2 tells me that the compiler has resolved it to the instance method but doesn't accept it because there is no Tiger instance in that context. Doesn't that tell you that whether Animal::eat refers to a static method or an instance method depends on the context?

What do you think about this?

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

Martyjee
Posts: 32
Joined: Tue Oct 06, 2015 9:06 am
Contact:

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

Post by Martyjee »

Hi Paul,

Very interesting problem!

I never assumed that inferring which method to choose does not depend on the context.
Of course it depends on the context, I agree with you. But that context is same as the compiler's context.
My point is that you don't need an actual instance of an object to reference it as Class::instanceMethod.
However, the message in line //2 tells me that the compiler has resolved it to the instance method but doesn't accept it because there is no Tiger instance in that context.
The compiler tries its best to resolve method references: in this case, it could not find a parameter list that matches, namely (Animal a, Mouse m), so in this case it assumed that you wanted to use the instance method with only one parameter (Mouse). Obviously, that is not possible.

I'm starting to think that your referral to the "context that supplies an instance" is the same as my referral to "parameter lists must match" ;) If that is the case, I agree partially, as the context needs to supply a reference to an object. Consider the code below. The context provides an implicit reference to an Animal by means of specifying BiConsumer<Animal, Mouse>. However, the context does need to provide an instance at the time you use the method!

Code: Select all

class Mouse {    }
class Tiger{   }
class Animal {
    public void eat(Mouse m){    }
    public static void eat(Tiger t, Mouse m){    }
}

public class JavaApplication33 {
    public static void main(String[] args) {
        //The context does !not! supply any instance of Animal, but it compiles and it uses the instance method eat
        BiConsumer<Animal, Mouse> bC = Animal::eat; //1 
    }
}
Kind regards,

Martijn

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

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

Post by admin »

Ok, I think we are getting somewhere now :)
I think the cause of confusion is the phrase, "context supplies the instance". Of course, this supply happens at run time. Indeed, no objects are created at compile time anyway. Compiler only checks that the "context" in which it will be invoked is capable of supplying the instance at run time.
If you like our products and services, please help us by posting your review here.

johnlong
Posts: 197
Joined: Mon Jun 20, 2016 5:06 pm
Contact:

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

Post by johnlong »

Hello

Having difficulties understanding this question.
Could you please advise how can I substitute those method references with the lambda expressions?

process(fnames, t::eat);
process(fnames, t::calories);
process(fnames, TestClass::size);

johnlong
Posts: 197
Joined: Mon Jun 20, 2016 5:06 pm
Contact:

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

Post by johnlong »

Got it
process(fnames, t::eat); -> process( fnames, a -> t.eat(a));
process(fnames, t::calories); -> process( fnames, a -> t.calories(a));
process(fnames, TestClass::size); -> process( fnames, a -> TestClass.size(a));

Q1) So how to differentiate between var::instanceMethod or class:instanceMethod?
Q2) Lambda expression for var::instanceMethod and class:StaticMethod has the same syntax?

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

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

Post by admin »

johnlong wrote: Q1) So how to differentiate between var::instanceMethod or class:instanceMethod?
Q2) Lambda expression for var::instanceMethod and class:StaticMethod has the same syntax?
Not sure what you mean by differentiation. var::instanceMethod uses the variable name, while class:instanceMethod uses the class name.


It is explained here is more detail: https://docs.oracle.com/javase/tutorial ... ences.html
If you like our products and services, please help us by posting your review here.

johnlong
Posts: 197
Joined: Mon Jun 20, 2016 5:06 pm
Contact:

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

Post by johnlong »

Thanks.

ramy6_1
Posts: 124
Joined: Wed Feb 12, 2014 2:44 am
Contact:

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

Post by ramy6_1 »

Hello ,

I have the same above question as I still have problems understanding method and constructor reference

Could you please advise how can I substitute those method references with the lambda expressions?

process(fnames, t::eat);
process(fnames, t::calories);
process(fnames, TestClass::size);

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

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

Post by admin »

Check out this article: http://enthuware.com/index.php/home/115
Now, look at the method eat, for example, and turn it into a lambda expression as described in above article.
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.1871 :

Post by lenalena »

I'm having trouble understanding the explanation for why last option (Tiger::eat) is wrong. I understand the situation with the static reference. However, the part
"Which meaning is implied depends on the context in which it is used. Here, the context does not supply any instance of Tiger class.
How DOES the context supply the instance of Tiger class in this situation? It's clear when the method reference is called on an instance variable (such as t::eat). But in case of Tiger::eat - how would the instance of Tiger class be supplied by the context. Could you, please, show an example?

Thank you.

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

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

Post by admin »

The object is supplied by the context if it is available in the context. For example,

Code: Select all

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
(Ref: https://docs.oracle.com/javase/tutorial ... ences.html )
The compareToIgnoreCase method has to operate on the stringArray. So the context does have String objects in that array. Therefore, it can supply two strings from the array to call that method. There is no need for a separate String object for the method to be invoked.

In the last option of this question, process(fnames, Tiger::eat);, there is no Tiger object in the context.

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

ssoltanid
Posts: 3
Joined: Wed Nov 12, 2014 3:03 pm
Contact:

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

Post by ssoltanid »

Hi, I'm pretty sorry but I couldn't understand the answer at all.

How this line :

Code: Select all

process(fnames, t::calories);
can call this method :

Code: Select all

public static void process(List<String> names, Carnivore c) {
        c.eat(names);
    }
Since the method's waiting for 2 args : List<String> names and Carnivore c.

In fact I couldn't understand how we can pass t::calories to Carnivore c, since t:calories return a int.

Sorry about the question.

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

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

Post by admin »

Carnivore has only one abstract method "int eat(List<String> foods)".
To implement this interface using a lambda expression, you require a reference to a method with the same parameter and return types. t::calories refers to Carnivore's default method "int calories(List<String> food)", which satisfies this requirement. So there is no issue.
If you like our products and services, please help us by posting your review here.

zukras
Posts: 5
Joined: Fri Jun 02, 2017 4:57 pm
Contact:

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

Post by zukras »

Tiger::eat is a valid method reference that can mean to refer either to a static method eat of Tiger class or to an instance method of any arbitrary instance of Tiger class. Which meaning is implied depends on the context in which it is used. Here, the context does not supply any instance of Tiger class.
How would look like context where I could call Tiger::eat?

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

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

Post by admin »

Let's say you have this interface:

Code: Select all

interface SomeI{
  void m(Tiger t, List<String> list);
}
You can then do the following in the main of TestClass:

Code: Select all

class TestClass{
  public static void main(String[] args) {
      SomeI s = Tiger::eat;
  }
}
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.1871 :

Post by __JJ__ »

Martyjee wrote:
Thu Oct 15, 2015 3:51 am
Hi Paul,
As per the code that is given, if you use Tiger::eat, you are implying that you want to call eat method on Tiger class (as opposed to Tiger instance)
No, that is not true, you are implying that you want to use the code that is defined in the eat method of the type Tiger! The Oracle docs define 4 flavours of method references, and this flavour (the most confusing one) is what they call "Reference to an Instance Method of an Arbitrary Object of a Particular Type". Remember that we are dealing with lambda expressions and method references here, not actual method calls!
There is no context here that provides you a Tiger instance.
You don't need a Tiger instance, and the eat method does not have to be static to reference it as Tiger::eat.
Could you please show with code what you mean?
I will give a code example that I came up with (assume appropriate import statements):

Code: Select all

public class Example {
    public static void main(String[] args) {
        Mother mother = new Mother();
        Child child = new Child();
        mother.giveOrder(child::eat);   //1 Reference to an instance method of a particular object
        mother.giveOrder(Knife::slice); //2 Reference to an instance method of an arbitrary object of a particular type
        mother.giveOrder(Knife::stab);  //3 Reference to a static method
    }
}
[SNIP]
    }
}
As you can see at line //2 there is no instance needed to reference Knife::slice
It compiles fine! The context is created at line //4 where the actual Knife object is first needed!
It looks like the "slice" method does not have the required parameters to be treated as an Eater, but actually it does, because the compiler inserts some arbitrary object reference to the parameter list as its first parameter! The program will even cause a compilation error at //2 if we rename the method "stab" to "slice", because their parameter lists are effectively the same when used as a lambda expression!!

HTH,

Martijn
Have to say, this is very clever. I have been looking at it and having a play and it's quite something how it manages to work.
I think it could be one of those things that once you used it a number of times you'd just see it as normal, but when you first come across it, it's a bit of a head scratcher. But thank you for putting together this example. I suggest people have a play with it themselves and reduce it down to just the interesting stuff ie the stuff pertinent to

Code: Select all

mother.giveOrder(Knife::slice);

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

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

Post by __JJ__ »

After playing around with Martijn's code, I was able to hack the code in the question to get it to work with the Tiger::eat method reference; not to prove the question/answer wrong (it's not, as far as I can see) but just to try to get a handle on how Martijn's example works.
So FWIW here it is

Code: Select all

import java.util.function.*;
import java.util.*;

interface Carnivore{
    default int calories(List<String> food){
        return food.size()*100;
    }
    int eat(Tiger t, List<String> foods);
}
class Tiger { 
    public int eat(List<String> foods){
        System.out.println("Eating "+foods);
        return foods.size()*200;
    }
}
public class TestClass4 {
    public static int size(List<String> names){
        return names.size()*2;
    }
    public static void process(List<String> names, Carnivore c){
        c.eat(new Tiger(), names);
    }
    
    public static void main(String[] args) {
        List<String> fnames = Arrays.asList("a", "b", "c");
        process(fnames, Tiger::eat);

   }
}
The key thing it seems is that the interface method has to have as an (its first?) argument the class type of the instance that will be supplied at runtime. That type must have a method that takes the rest of the arguments defined in the interface's abstract method (here, just one extra argument). Then at runtime the invocation of the functional interface method will supply an instance

Code: Select all

c.eat(new Tiger(), names);
and that instance is what the instance method is invoked on.
It really is very clever.

Sorry if any of this is wrong; I'm just trying to understand it myself and thought it might help somebody.

tylrdr
Posts: 6
Joined: Sun Sep 01, 2019 9:33 am
Contact:

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

Post by tylrdr »

__JJ__ wrote:
Fri Jul 27, 2018 7:27 pm
After playing around with Martijn's code, I was able to hack the code in the question to get it to work with the Tiger::eat method reference
Thank you for the example.

To make it even more understandable, in your example process(fnames, Tiger::eat) can be replaced with:

1) This equivalent lambda:
process(fnames, (a,b)->a.eat(b));

2) Or this equivalent anonymous class:
process(fnames, new Carnivore(){
@Override
public int eat(Tiger t, List<String> foods) {
return t.eat(foods);
}
});

It clearly works but I can't find any rule that explains why it works like this and how it works exactly. If anyone can figure out what the rule is about how exactly Java finds "context", would be helpful.
Admin said "The object is supplied by the context if it is available in the context." Sadly I don't quite understand this because the word context is too abstract for me... can anyone define "context" or is there a better way to describe this behavior please?

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

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

Post by admin »

Exact and complete rules are given in Section 15.13 of JLS 11.
If you like our products and services, please help us by posting your review here.

Post Reply

Who is online

Users browsing this forum: No registered users and 51 guests