Ответ
Слева от оператора :: указывается цель ссылки. Возможные варианты:
-
Имя класса — для ссылки на статический метод.
Function<String, Integer> parser = Integer::parseInt; // Статический метод -
Ссылка на объект (или выражение, её возвращающее) — для ссылки на метод экземпляра конкретного объекта.
String prefix = "ERROR-"; Function<String, String> formatter = prefix::concat; // Метод конкретного объекта -
Имя класса — для ссылки на конструктор.
Supplier<List<String>> supplier = ArrayList::new; // Конструктор -
Имя класса — для ссылки на метод экземпляра, который будет вызван у объекта, переданного позже (первый параметр становится целевым объектом).
Function<String, Integer> lengthGetter = String::length; // length() будет вызван у строки BiPredicate<String, String> checker = String::equals; // equals() вызван у первой строки
Практический пример с forEach:
List<String> names = Arrays.asList("Alice", "Bob");
// Ссылка на статический метод класса
names.forEach(System.out::println); // println - статический? Нет! Это пример 2.
// System.out — это статическое поле (объект PrintStream), поэтому это ссылка на метод конкретного объекта.
// Ссылка на метод экземпляра произвольного объекта (пример 4)
names.stream().map(String::toUpperCase).forEach(System.out::println);
// toUpperCase() будет вызван для каждого элемента потока.
Итог: Слева от :: может быть класс (для статических методов, конструкторов или методов экземпляра) или ссылка на существующий объект.