CodeGym /Blog Java /Poland /Słowo kluczowe return w Javie
Autor
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Słowo kluczowe return w Javie

Opublikowano w grupie Poland
Jak wiadomo, język Java jest obiektowym językiem programowania. Innymi słowy, podstawową koncepcją, czyli fundamentem fundamentu, jest to, że wszystko jest obiektem. Obiekty są opisywane za pomocą klas. Z kolei klasy definiują stan i zachowanie. Na przykład, konto bankowe może mieć stan w postaci ilości pieniędzy na koncie i mieć zachowania, które zwiększają i zmniejszają saldo konta. W Javie zachowania są implementowane za pomocą metod. Nauka definiowania metod pojawia się na samym początku nauki języka Java. Znajduje się na przykład w oficjalnym samouczku Oracle pod nagłówkiem „Definiowanie metod” Należy zwrócić uwagę na dwie ważne kwestie:
  • Każda metoda ma swoją sygnaturę. Składa się ona z nazwy metody i jej parametrów wejściowych.
  • W przypadku metod należy określić typ zwracany. Typ zwracany metody deklaruje się w jej deklaracji.
Typ zwracany nie jest częścią sygnatury metody. Ponownie, jest to konsekwencja faktu, że Java jest silnie typowanym językiem, a kompilator chce z wyprzedzeniem wiedzieć jak najwięcej — jakie typy są używane i gdzie. Ma to na celu uchronienie nas przed błędami. W gruncie rzeczy wszystko to jest dobrze uzasadnione. I wydaje mi się, że to po raz kolejny zaszczepia w nas kulturę obchodzenia się z danymi. Tak więc dla metod określany jest typ zwracanej wartości. W Javie return to słowo kluczowe, które jest rzeczywiście używane do zwracania.

Do czego służy return w Javie (Java return)

Słowo kluczowe return jest instrukcją zarządzania przepływem danych tak, jak opisano w samouczku Oracle tutaj. Możesz również przeczytać o tym, jak zwracać wartości w sekcji „Zwracanie wartości z metody” w oficjalnym samouczku. Kompilator dokładnie śledzi, czy może zapewnić, że wartość zwracana metody jest zgodna z określonym zwracanym typem metody. Rozważmy to na przykładzie Tutorialspoint Online IDE. Spójrzmy na pierwotny przykład:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}
Jak widzimy, wykonywana jest tu metoda main, która jest punktem wejścia programu. Linie kodu wykonywane są od góry do dołu. Nasza metoda main nie może zwrócić wartości. Jeśli spróbujemy zwrócić tam jakąś wartość, otrzymamy błąd: "Błąd: Metoda main musi zwracać wartość typu void". W związku z tym metoda po prostu wyświetla dane na ekranie. Przenieśmy teraz łańcuch znaków do oddzielnej metody generowania wiadomości:

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println(getHelloMessage());
    }
    
    public static String getHelloMessage() {
        return "Hello World";
    }
    
}
Jak widać, użyliśmy słowa kluczowego return do wskazania wartości zwracanej, którą następnie przekazaliśmy do metody println. Deklaracja metody getHelloMessage wskazuje, że metoda zwróci String. Pozwala to kompilatorowi sprawdzić, czy działanie metody jest zgodne z tym, jak została ona zadeklarowana. Oczywiście typ zwracany określony w deklaracji metody może być szerszy niż typ wartości faktycznie zwracanej w kodzie, tzn. ważne jest, aby konwersja typu była możliwa. W przeciwnym razie w czasie kompilacji pojawi się błąd: „Błąd: typy niezgodne”. Przy okazji, prawdopodobnie masz pytanie: Dlaczego return jest uważane za twierdzenie służące do kontroli przepływu danych? Ponieważ może zakłócić normalny odgórny przepływ programu. Przykład:

public class HelloWorld {

    public static void main(String[] args) {
        if (args.length == 0) {
            return;
        }
        for (String arg : args) {
            System.out.println(arg);
        }
    }
    
}
Jak widać na przykładzie, zakłócamy metodę main w programie Java, jeśli zostanie wywołana bez argumentów. Należy pamiętać, że jeśli masz kod po twierdzeniu return w Javie (Java return), to nie będzie on dostępny. Nasz inteligentny kompilator zauważy to i uniemożliwi uruchomienie takiego programu. Na przykład ten kod się nie kompiluje:

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");
 }
Można to obejść, korzystając z pewnego nieczystego zagrania. Na przykład do debugowania lub z innego ważnego powodu. Powyższy kod można naprawić, opakowując instrukcję return w blok if:

if (2==2) {
    return;
}

Deklaracja return podczas obsługi błędów

Jest jeszcze coś, co może sprawiać kłopoty - użycie return w połączeniu z obsługą błędów. Chcę od razu powiedzieć, że używanie return w bloku catch jest bardzo, bardzo złym nawykiem, więc powinieneś tego unikać. Ale potrzebujemy przykładu, prawda? Oto i on.

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);
        }
    }
    
}
Na pierwszy rzut oka wydaje się, że powinno zostać zwrócone 2, ponieważ finally jest zawsze wykonywane. Ale nie, zwrócona wartość będzie wynosić 1, a zmiana zmiennej w bloku finally zostanie zignorowana. Ponadto załóżmy, że value zawiera obiekt i napiszemy value = null w bloku finally. Wtedy ten obiekt, nie null, zostałby zwrócony w bloku catch. Ale deklaracja return w bloku finally działałaby poprawnie. Oczywiście twoi współpracownicy raczej nie podziękują ci za niespodzianki z twierdzeniami return. Słowo kluczowe return w Javie - 1

void.class

I na koniec. Istnieje pewna dziwna konstrukcja: void.class. Hmm. Dlaczego i co to oznacza? Mamy różne frameworki i trudne przypadki związane z Java Reflection API, w których może to być bardzo przydatne. Na przykład, możesz sprawdzić jaki typ zwraca dana metoda:

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);
        }
    }
}
Może to być przydatne w frameworkach testowych, w których trzeba zamienić faktyczny kod w metodach. Lecz aby to zrobić, musisz zrozumieć, jak zachowuje się metoda (tj. jaki typ zwraca). Istnieje również drugi sposób implementacji metody main w powyższym kodzie:

public static void main(String[] args) {
        for (Method method : HelloWorld.class.getDeclaredMethods()) {
            System.out.println(method.getReturnType() == Void.TYPE);
        }
 }
Możesz przeczytać całkiem interesującą dyskusję na temat różnicy między nimi na Stack Overflow: Czym się różni java.lang.Void i void?
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION