0
class Dish {
  public int getCalories() {
    return calories;
  }

public static final List<Dish> menu = Arrays.asList(
      new Dish("pork", false, 800, Dish.Type.MEAT),
      new Dish("beef", false, 700, Dish.Type.MEAT),
      new Dish("chicken", false, 400, Dish.Type.MEAT),
      new Dish("french fries", true, 530, Dish.Type.OTHER),
      new Dish("rice", true, 350, Dish.Type.OTHER),
      new Dish("season fruit", true, 120, Dish.Type.OTHER),
      new Dish("pizza", true, 550, Dish.Type.OTHER),
      new Dish("prawns", false, 400, Dish.Type.FISH),
      new Dish("salmon", false, 450, Dish.Type.FISH)
  );
}

How is the following Dish::getCalories method reference valid when summingInt requires a ToIntFunction? I am asking because the signature of getcalories does not match the signature of applyAsInt abstract method of ToIntFunction

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
karan mirani
  • 168
  • 1
  • 3
  • 7
  • It takes a Dish and returns an int. An equivalent lambda would be `d -> d.getCalories();` – Ravindra Ranwala Aug 22 '20 at 04:48
  • I understand that. However, the signature of `getCalories` does not match the signature of `applyAsInt` abstract method in `ToIntFunction` – karan mirani Aug 22 '20 at 04:51
  • @karanmirani Look at method reference to understand how it works when a functional interface is needed. – Jean-Baptiste Yunès Aug 24 '20 at 09:09
  • 1
    More duplicates: [Reference to methods with different parameters in Java8](https://stackoverflow.com/q/27754335/2711488), [Java Lambda Expression](https://stackoverflow.com/q/29802633/2711488), … – Holger Aug 24 '20 at 12:52

3 Answers3

3

I am asking because the signature of getCalories does not match the signature of applyAsInt abstract method of ToIntFunction.

Actually, it does match.

ToIntFunction<T> is a functional interface since it has the annotation @FunctionalInterface. That means that the signature of applyAsInt matches any class1, method reference or lambda that provides a method that maps from <T> to int.

In this case Dish::getCalories is mapping a Dish to an int by calling the Dish instance's getCalories() method on it:

  • The source for the mapping is the instance of Dish
  • The result of the mapping is the result of calling getCalories() on the instance2.

(Note: this is an intuitive explanation. For a technical explanation, refer to the relevant parts of the JLS.)


1 - In the case where an instance of a class is supplied, there can only be one method in the class API that satisfies the functional interface requirement. So, hypothetically, if Dish was declared with the @ToIntFunction<Dish> annotation, it could not expose two methods that take no arguments and return an int.

2 - Normal method overriding rules apply. If the actual object is an instance of a subtype of Dish, and it overrode getCalories(), then the mapping would call that method, not the overridden method.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • > That means that the signature matches any **class**, method reference or lambda that provides a (single) method that maps from to int. So if a **class** has other methods ie if **Dish** had other methods, then the _method reference_ would be invalid? Also, **getCalories** takes no argument and returns a **Dish** instance whereas **applyAsInt** takes a **** and returns an **int** – karan mirani Aug 22 '20 at 17:30
0

For anyone wondering why it works. It works because this is an implicit first argument to every method of an object. Hence, the signature of Dish::getCalories is the same as applyAsInt of ToIntFunction. Please correct me if I am wrong.

karan mirani
  • 168
  • 1
  • 3
  • 7
0

Emmm, well, it works here! I'm not sure if there is still something concealed here.

import java.util.List;

import static java.util.stream.Collectors.summingInt;

import java.util.Arrays;

class Dish {

    public int getCalories() {
        return calories;
    }

    public static final List menu = Arrays.asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT),
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER),
            new Dish("rice", true, 350, Dish.Type.OTHER),
            new Dish("season fruit", true, 120, Dish.Type.OTHER),
            new Dish("pizza", true, 550, Dish.Type.OTHER),
            new Dish("prawns", false, 400, Dish.Type.FISH),
            new Dish("salmon", false, 450, Dish.Type.FISH)
    );

    public static void main(String[] args) {
        int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
        System.out.println(totalCalories);
    }

    String name;

    int calories;

    Dish.Type type;

    public Dish(String name, boolean b, int calories, Dish.Type type) {
        super();
        this.name = name;
        this.calories = calories;
        this.type = type;
    }

    enum Type {
        MEAT, FISH, OTHER
    }

}
XuYanhang
  • 31
  • 1