🔥 🚀 Важно для всех, кто работает с 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».