Оператор :: (двойное двоеточие) в Java 8: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Оператор :: (Double Colon) в Java 8: от новичка до профи

Вы когда-нибудь писали код на Java 8 и видели странную конструкцию вроде System.out::println или String::length? Выглядит как магия, не так ли? На самом деле, это не магия, а один из самых элегантных инструментов современной Java — оператор двойное двоеточие (он же double colon operator или ссылка на метод).

В этой статье мы разберём оператор :: от самых основ до продвинутых приёмов. Вы научитесь писать более чистый, лаконичный и читаемый код. Поехали!

Что такое оператор :: и зачем он нужен?

Оператор :: (double colon) был введён в Java 8 как синтаксический сахар для лямбда-выражений. Если лямбда-выражение просто вызывает существующий метод, вы можете заменить его ссылкой на метод. Это делает код короче и выразительнее.

Сравните сами:

// Лямбда-выражение
list.forEach(s -> System.out.println(s));

// Ссылка на метод (оператор ::)
list.forEach(System.out::println);

Второй вариант не только короче, но и сразу говорит: "Для каждого элемента вызови метод println у System.out". Красота!

Четыре вида ссылок на метод

В Java 8 существует четыре основных типа method reference. Давайте разберём каждый с примерами.

1. Ссылка на статический метод

Синтаксис: ClassName::staticMethodName

Пример: подсчёт количества чётных чисел в списке.

import java.util.Arrays;
import java.util.List;

public class StaticMethodReference {
    public static boolean isEven(Integer number) {
        return number % 2 == 0;
    }

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        
        // Лямбда
        long countLambda = numbers.stream()
                .filter(n -> StaticMethodReference.isEven(n))
                .count();
        
        // Ссылка на статический метод
        long countRef = numbers.stream()
                .filter(StaticMethodReference::isEven)
                .count();
        
        System.out.println("Чётных чисел: " + countRef); // 3
    }
}

Обратите внимание: мы не вызываем метод isEven — мы просто передаём ссылку на него. Java сама подставит каждый элемент списка в качестве аргумента.

2. Ссылка на метод экземпляра конкретного объекта

Синтаксис: objectReference::instanceMethodName

Этот вид используется, когда у вас уже есть конкретный объект, и вы хотите вызвать его метод.

import java.util.Arrays;
import java.util.List;

public class InstanceMethodReference {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Анна", "Борис", "Виктор");
        
        // Создаём объект StringBuilder
        StringBuilder sb = new StringBuilder("Имена: ");
        
        // Лямбда
        names.forEach(name -> sb.append(name).append(" "));
        
        // Ссылка на метод экземпляра
        // (но append возвращает StringBuilder, поэтому используем лямбду)
        // А вот для System.out.println - идеально:
        names.forEach(System.out::println);
        
        System.out.println(sb.toString()); // Имена: Анна Борис Виктор
    }
}

Классический пример — System.out::println. System.out — это конкретный объект PrintStream, а println — его метод.

3. Ссылка на метод экземпляра произвольного объекта определённого типа

Синтаксис: ClassName::instanceMethodName

Этот вид часто путают с предыдущим. Здесь мы не указываем конкретный объект — вместо этого мы говорим: "Возьми любой объект этого класса и вызови у него метод".

import java.util.Arrays;
import java.util.List;

public class ArbitraryObjectReference {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("яблоко", "груша", "апельсин");
        
        // Лямбда
        words.stream()
                .map(word -> word.toUpperCase())
                .forEach(System.out::println);
        
        // Ссылка на метод экземпляра произвольного объекта
        words.stream()
                .map(String::toUpperCase)  // toUpperCase вызывается у каждого String
                .forEach(System.out::println);
    }
}

Здесь String::toUpperCase означает: "Для каждого элемента (который является String) вызови метод toUpperCase". Java сама понимает, что первый аргумент — это объект, у которого вызывается метод.

4. Ссылка на конструктор

Синтаксис: ClassName::new

Позволяет создавать новые объекты через ссылку на конструктор.

import java.util.function.Supplier;

public class ConstructorReference {
    public static void main(String[] args) {
        // Ссылка на конструктор без аргументов
        Supplier<StringBuilder> supplier = StringBuilder::new;
        
        // Создаём новый StringBuilder через ссылку
        StringBuilder sb = supplier.get();
        sb.append("Привет, мир!");
        System.out.println(sb.toString()); // Привет, мир!
        
        // Более сложный пример с массивом
        // (требуется IntFunction)
        java.util.function.IntFunction<String[]> arrayCreator = String[]::new;
        String[] array = arrayCreator.apply(5);
        System.out.println("Длина массива: " + array.length); // 5
    }
}

Где применяется оператор :: на практике?

Ссылки на методы особенно полезны при работе с Stream API и функциональными интерфейсами. Рассмотрим несколько реальных сценариев.

Сортировка с Comparator

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class SortingExample {
    public static void main(String[] args) {
        List<String> cities = Arrays.asList("Москва", "Санкт-Петербург", "Казань", "Новосибирск");
        
        // Сортировка по длине строки
        cities.sort(Comparator.comparingInt(String::length));
        System.out.println(cities); // [Казань, Москва, Новосибирск, Санкт-Петербург]
        
        // Сортировка в обратном порядке
        cities.sort(Comparator.comparingInt(String::length).reversed());
        System.out.println(cities); // [Санкт-Петербург, Новосибирск, Москва, Казань]
    }
}

Фильтрация и преобразование данных

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Анна", "Борис", "Виктор", null, "Галина");
        
        // Убираем null, преобразуем в верхний регистр, собираем в список
        List<String> processed = names.stream()
                .filter(java.util.Objects::nonNull)  // Ссылка на статический метод
                .map(String::toUpperCase)            // Ссылка на метод экземпляра
                .collect(Collectors                

Блоги

Рекомендаций по книгам