Как работает оператор return в Java

🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!

В этой статье разберемся с ключевым словом return в Java — когда и зачем его следует применять. Вы можете выбрать удобный для вас формат — посмотреть видеоурок на английском языке, либо же прочитать текстовую версию ниже.

Как известно, Java — это объектно-ориентированный язык программирования. Другими словами, его основополагающая концепция заключается в том, что все является объектом. А объекты описываются с помощью классов.

Классы задают состояние и поведение. Например, у банковского счета может быть состояние в виде количества денег на счете, а поведение — методы, увеличивающие или уменьшающие баланс. В Java поведение реализуется с помощью методов.

Изучение методов — одна из первых тем в курсе по Java. Например, в официальном руководстве Oracle этому посвящён раздел под названием “Определение методов“.

Здесь следует обратить внимание на два важных момента:

  • Каждый метод имеет свою сигнатуру. Сигнатура состоит из имени метода и его входных параметров.
  • Для методов необходимо указывать тип возвращаемого значения. Возвращаемый тип указывается в объявлении метода.

Но возвращаемый тип не является частью сигнатуры метода.

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

Итак, для методов обязательно указывается тип возвращаемого значения. А для возврата значения из метода используется ключевое слово return.

Что делает ключевое слово return в Java

Ключевое слово return относится к операторам управления потоком (control flow) — об этом говорится в документации Oracle.

Подробнее о возврате значений из метода можно прочитать в разделе “Returning a Value from a Method” (“Возврат значения из метода”) официального руководства.

Компилятор строго следит, чтобы возвращаемое значение метода соответствовало объявленному типу.

Как вариант, можно рассмотреть пример с помощью онлайн IDE Tutorialspoint.

Начнем с классики:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}
Run Code

Как мы видим, здесь выполняется метод main, который является точкой входа в программу. Код выполняется сверху вниз, построчно. Метод main не может вернуть значение. Если попытаться вернуть что-либо из него, возникнет ошибка: "Error: Main method must return a value of type void". Следовательно, метод просто выводит сообщение на экран. А теперь вынесем строку в отдельный метод, который будет ее возвращать:

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println(getHelloMessage());
    }

    public static String getHelloMessage() {
        return "Hello World";
    }

}
Run Code

Здесь строка из метода возвращается с помощью ключевого слова return, а потом просто передается в метод println. В объявлении метода getHelloMessage указано, что метод будет возвращать строку. Это позволяет компилятору проверить, что поведение метода соответствует его объявлению.

При этом тип, указанный в сигнатуре, может быть шире, чем конкретное возвращаемое значение — главное, чтобы приведение было допустимым. В противном случае при компиляции мы получим ошибку "Error: incompatible types".

Возникает довольно логичный вопрос: Почему return считается оператором потока управления? Потому что он может нарушить обычную последовательность выполнения кода сверху вниз. Например:

public class HelloWorld {

    public static void main(String[] args) {
        if (args.length == 0) {
            return;
        }
        for (String arg : args) {
            System.out.println(arg);
        }
    }

}
Run Code

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

Важно помнить: все, что написано после оператора return, выполняться не будет. Компилятор обнаружит это и не позволит скомпилировать такую программу. Например, этот код не скомпилируется:

public static void main(String[] args) {
        System.out.println("1");
        return;
// we use output method after return statement, which is incorrect
        System.out.println("2");
 }
Run Code

Но есть способ обойти это (иногда это нужно, например, при отладке). К примеру, можно спрятать оператор return внутри if:

if (2==2) {
    return;
}
Run Code

Оператор return при обработке ошибок

Есть еще интересный момент — можно использовать оператор return в сочетании с обработкой ошибок. Но сразу стоит отметить, что писать return внутри блока catchплохая практика, и от этого стоит воздерживаться. Но в качестве примера посмотрите на этот код:

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Value: " + getIntValue());
    }

    public static int getIntValue() {
        int value = 1;
        try {
            System.out.println("Something terrible happens");
            throw new Exception();
        } catch (Exception e) {
            System.out.println("Cached value: " + value);
            return value;
        } finally {
            value++;
            System.out.println("New value: " + value);
        }
    }

}
Run Code

На первый взгляд кажется, что метод вернет 2, так как блок finally всегда выполняется. Но нет, возвращаемое значение будет равно 1, а изменение переменной в блоке finally будет проигнорировано. Более того, если бы value был объектом, и мы в finally присвоили бы ему null, то вернулся бы сам объект, а не null — потому что возвращаемое значение было зафиксировано в catch.

Но если бы return находился внутри finally, он бы перебил return из catch, и метод вернул бы уже новое значение.

void.class

И напоследок — немного странная конструкция: void.class. Зачем она нужна и что означает?
На самом деле, в некоторых фреймворках и при использовании Java Reflection API это бывает весьма полезно. Например, с её помощью можно определить возвращаемый тип метода:

import java.lang.reflect.Method;

public class HelloWorld {

    public void getVoidValue() {
    }

    public static void main(String[] args) {
        for (Method method : HelloWorld.class.getDeclaredMethods()) {
            System.out.println(method.getReturnType() == void.class);
        }
    }
}
Run Code

Это может быть полезно в тестовых фреймворках, где требуется заменить реализацию метода. Но для этого нужно понимать, как ведет себя метод (т.е. какой тип он возвращает).

Есть и другой способ, через Void.TYPE:

public static void main(String[] args) {
        for (Method method : HelloWorld.class.getDeclaredMethods()) {
            System.out.println(method.getReturnType() == Void.TYPE);
        }
 }
Run Code

На Stack Overflow можете найти интересный материал, в котором обсуждают разницу между void.class и Void.TYPE: What is the difference between java.lang.Void and void?

Перевод статьи «Java return Keyword».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх