Pattern мatching. Паттерн типов

Последнее обновление: 27.09.2025

Паттерн типов (type pattern) позволяет сопоставить значение или результат выражения с определенным типом. Для применения этого паттерна можно использовать оператор instanceof или выражения switch

instanceof

Традиционно оператор instanceof позволял проверить принадлежность объекта определенному типа. Но в последних версиях Java при его применении также можно автоматически преобразовать объект в сопоставленный тип и передать преобразованное значение в определенную переменную. Например, пусть у нас есть следующая система типов:

// класс человека
class Person {
     
    private String name;
    String getName() {return name;}
    
    Person(String name){ this.name=name;  }
}


// служащий некоторой компании 
class Employee extends Person{
 
    private String company;  // компания, где работает человек
    String getCompany(){return company;}
     
    Employee(String name, String company) {
     
        super(name);
        this.company = company;
    }
}
// класс клиента банка
class Client extends Person{
       
    private String bank; // банк клиента
    String getBank(){ return bank; }
       
    Client(String name, String bank) {
       
        super(name);
        this.bank = bank;
    }
}

Рассмотрим применение паттерна типов с помощью оператора instanceof:

class Program{
      
    public static void main(String[] args) {
          
        Person tom = new Person("Tom");
        Person bob = new Employee("Bob", "Google");
        Person sam = new Client("Sam", "Sberbank");

        printPerson(tom);       // Person Tom
        printPerson(bob);       // Bob works in Google
        printPerson(sam);       // Sam is client of Sberbank
    }

    static void printPerson(Person person){
        if(person instanceof Employee empl){
        
            System.out.printf("%s works in %s\n", empl.getName(), empl.getCompany());
        }
        else if(person instanceof Client cl){
        
            System.out.printf("%s is client of %s\n", cl.getName(), cl.getBank());
        }
        else{
                                
            System.out.println("Person " + person.getName());
        }
    }
}

В статическом методе printPerson получаем объект Person и с помощью оператора instanceof проверяем принадлежность объекта определенному типу. Так, выражение

person instanceof Employee empl

проверяет, представляет ли параметр person класс Employee, и если представляет (то есть оператор instanceof возвращает true), то передает ссылку на этот объект переменной empl типа Employee. И в дальнейшем мы можем использовать эту переменную empl и производить с ней различные операции.

Также можно использовать паттерн типов с instanceof в тернарном операторе:

class Program{
      
    public static void main(String[] args) {
             
        Person kate = new Client("Kate", "Sberbank");
        printEmplCompany(kate);  // ничего

        Person sam = new Employee("Sam", "Yandex");
        printEmplCompany(sam);  // Yandex

        Person bob = new Employee("Bob", "VK");
        printEmplCompany(bob);  // VK
    }

    static void printEmplCompany(Person p){

        String company = (p instanceof Employee empl) ?  empl.getCompany() : "";
        
        System.out.println(company);
    }
}

Выражения/конструкции switch

Конструкции/выражения switch позволяют упростить паттерн типов и применение проверки типов:

class Program{
      
    public static void main(String[] args) {
          
        Person tom = new Person("Tom");
        Person bob = new Employee("Bob", "Google");
        Person sam = new Client("Sam", "Sberbank");

        printPerson(tom);       // Person Tom
        printPerson(bob);       // Bob works in Google
        printPerson(sam);       // Sam is client of Sberbank
    }

    static void printPerson(Person person){
        switch(person){
            case Employee empl ->
                System.out.printf("%s works in %s\n", empl.getName(), empl.getCompany());
            case Client cl ->
                System.out.printf("%s is client of %s\n", cl.getName(), cl.getBank());
            default ->
                System.out.println("Person " + person.getName());
        }
    }
}

Выражения типа case Employee empl аналогично оператору instaceof проверяют принадлежность значения типу Employee и при положительном результате передаются ссылку на объект в переменную empl.

Стоит отметить, что если сопоставляем с типом, то нам надо обязательно указать переменную. Например, мы не можем написать следующим образом:

switch(person){
    case Employee ->
        System.out.println("Person is Employee");

После типа Employee нам обязательно надо указать переменную. Однако в случае выше переменная не требуется - даже если мы ее определим, мы ее никак не используем. И в этом случае можно указать прочерк _ (так называемая "неименованная переменная"):

static void printPerson(Person person){
    switch(person){
        case Employee _ ->
            System.out.println("Person is Employee");
        
        case Client _ ->
            System.out.println("Person is Client");
        
        default ->
            System.out.println("Person " + person.getName());
    }
}

Обработка null

Теоретически да и практически переменная может хранить значение null. При использовании оператора instanceof мы не столкнемся с проблемами, так как этот оператор возвращает true, если выражение не равно null:

class Program{
      
    public static void main(String[] args) {
          
        Person tom = null;
        Person bob = new Employee("Bob", "Google");
        Person sam = new Client("Sam", "Sberbank");

        printPerson(tom);       // Person is Undefined
        printPerson(bob);       // Employee Bob
        printPerson(sam);       // Person Sam
    }
    static void printPerson(Person person){
        if(person instanceof Employee){
        
            System.out.println("Employee " + person.getName());
        }
        else if(person instanceof Person){
        
            System.out.println("Person " + person.getName());
        }
        else{       // обрабатываем null
                                
            System.out.println("Person Undefined");
        }
    }
}

В любом случае мы можем добавить проверку на null

else if(person == null){
    System.out.println("Person Undefined");
}

Но в любом случае, даже если выражение представляет тип Person, может иметь смысл сравнить его тип с типом Person, чтобы отсеить возможность нулевых ссылок:

else if(person instanceof Person){ ....

switch

Классический оператор switch генерирует исключение NullPointerException, если выражение, которое передается оператору switch, равно null. Этого можно избежать, добавив case null:

class Program{
      
    public static void main(String[] args) {
          
        Person tom = null;
        Person bob = new Employee("Bob", "Google");
        Person sam = new Client("Sam", "Sberbank");

        printPerson(tom);       // Person is Undefined
        printPerson(bob);       // Employee Bob
        printPerson(sam);       // Person Sam
    }

    static void printPerson(Person person){
        switch(person){

            case null ->     // обрабатываем null
                System.out.println("Person is Undefined");

            case Employee _ ->
                System.out.println("Employee " + person.getName());

            default ->
                System.out.println("Person " + person.getName());
        }
    }
}

Защитные условия паттерна или Guards

При сопоставления паттернов мы можем добавлять дополнительные условия или guards, которым также должны соответствовать выражения.

instanceof

Так, при использовании оператора instanceof мы можем по цепочке добавить условные выражения:

class Program{
      
    public static void main(String[] args) {
          
        Person bob = new Employee("Bob", "Yandex");
        Person sam = new Employee("Sam", "Sberbank");
        Person tom = new Person("Tom");

        printPerson(bob);       // Employee Bob works in Yandex
        printPerson(sam);       // Employee Sam
        printPerson(tom);       // Person Tom
    }
    static void printPerson(Person person){

        // по цепочке используем переменную empl
        if(person instanceof Employee empl && empl.getCompany() == "Yandex"){
        
            System.out.println("Employee " + empl.getName() + " works in Yandex");
        }
        else if(person instanceof Employee){
        
            System.out.println("Employee " + person.getName());
        }
        else if(person instanceof Person){
                                
            System.out.println("Person " + person.getName());
        }
    }
}

В данном случае выражение

if(person instanceof Employee empl && empl.getCompany() == "Yandex"){

    System.out.println("Employee " + empl.getName() + " works in Yandex");
}

указывает, что объект person не только должен представлять тип Employee, но и его компания должна быть "Yandex". Таким образом, подвыражение empl.getCompany() == "Yandex" и есть то, что называется "guard" или условие паттерна. И блок if выполняется, если верно это условие. Если условие не верно, то выполняется следующий блок:

else if(person instanceof Employee){
        
    System.out.println("Employee " + person.getName());
}

switch

ПРи использовании switch для ввода условий применяется оператор when:

class Program{
      
    public static void main(String[] args) {
          
        Person bob = new Employee("Bob", "Yandex");
        Person sam = new Employee("Sam", "Sberbank");
        Person tom = new Person("Tom");

        printPerson(bob);       // Employee Bob works in Yandex
        printPerson(sam);       // Employee Sam
        printPerson(tom);       // Person Tom
    }

    static void printPerson(Person person){
        switch(person){

            case Employee empl when empl.getCompany() == "Yandex" ->
                System.out.println("Employee " + empl.getName() + " works in Yandex");

            case Employee _ ->
                System.out.println("Employee " + person.getName());

            case Person _ ->
                System.out.println("Person " + person.getName());
        }
    }
}

То есть в данном случае выражение

when empl.getCompany() == "Yandex"

И есть защитное условие паттерна.

При этом защитные условия могут быть более сложными по структуре, объединяя несколько под условий:

switch(person){
    case Employee empl 
        when empl.getCompany() == "Yandex" && empl.name == "Bob" ->
            System.out.println("Bob works in Yandex");

В данном случае блок case выполняется, если объект person представляет тип Employee, но при этом также его имя - "Bob", а компания - "Yandex"

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.