CodeGym /Java Blog /Random-IT /Metodi in Java
John Squirrels
Livello 41
San Francisco

Metodi in Java

Pubblicato nel gruppo Random-IT
Ciao di nuovo! Nell'ultima lezione abbiamo familiarizzato con classi e costruttori e abbiamo imparato a crearne di nostri. Oggi conosceremo meglio i metodi Java, una parte essenziale delle classi. Un metodo in Java è un insieme di comandi che consentono di eseguire un'operazione specifica in un programma. In altre parole, un metodo è una funzione; qualcosa che la tua classe è in grado di fare. In altri linguaggi di programmazione, i metodi sono spesso chiamati "funzioni", ma in Java la parola "metodo" è più comune. :) Se ricordi, nell'ultima lezione abbiamo creato dei metodi semplici per una lezione di gatti , in modo che i nostri gatti potessero dire miagolare e saltare:

public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Pounce!");
    }

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";

        smudge.sayMeow();
        smudge.jump();
    }
}
sayMeow() e jump() sono metodi della nostra classe. E l'esecuzione di questi metodi produce il seguente output della console:
Meow!
Pounce!
I nostri metodi sono abbastanza semplici: inviano semplicemente il testo alla console. Ma in Java, i metodi hanno un compito importante: eseguono azioni sui dati di un oggetto. Cambiano i dati dell'oggetto, lo trasformano, lo visualizzano e fanno altre cose con esso. I nostri metodi attuali non fanno nulla con i dati dell'oggetto Cat . Diamo un'occhiata a un esempio più illustrativo:

public class Truck {

    int length;
    int width;
    int height;
    int weight;

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
Ad esempio, qui abbiamo una classe che rappresenta un Truck . Il semirimorchio ha lunghezza, larghezza, altezza e peso (di cui avremo bisogno in seguito). Nel metodo getVolume() , eseguiamo calcoli, convertendo i dati del nostro oggetto in un numero che rappresenta il suo volume (moltiplicando la lunghezza, la larghezza e l'altezza). Questo numero sarà il risultato del metodo. Si noti che la dichiarazione del metodo è scritta come public int getVolume . Ciò significa che questo metodo deve restituire un int . Abbiamo calcolato il valore di ritorno del metodo e ora dobbiamo restituirlo al programma che ha chiamato il nostro metodo. Per restituire il risultato di un metodo in Java, utilizziamo la parola chiave return. volume di ritorno;

Parametri del metodo Java

Possiamo passare valori chiamati "argomenti" a un metodo quando lo chiamiamo. La dichiarazione di un metodo include un elenco di variabili che ci dicono il tipo e l'ordine delle variabili che il metodo può accettare. Questo elenco è chiamato "parametri del metodo". Il metodo getVolume() della nostra classe Truck non definisce attualmente alcun parametro, quindi proviamo ad estendere il nostro esempio di truck. Crea una nuova classe chiamata BridgeOfficer . Questo è un agente di polizia in servizio su un ponte, che controlla tutti i camion di passaggio per vedere se il loro carico supera il peso consentito.

public class BridgeOfficer {

    int maxWeight;

    public BridgeOfficer(int normalWeight) {
        this.maxWeight = normalWeight;
    }

    public boolean checkTruck(Truck truck) {
        if (truck.weight > maxWeight) {
            return false;
        } else {
            return true;
        }
    }
}
Il metodo checkTruck accetta un argomento, un oggetto Truck , e determina se l'ufficiale consentirà o meno al camion di salire sul ponte. All'interno del metodo, la logica è abbastanza semplice: se il peso del camion supera il massimo consentito, il metodo restituisce false . Dovrà trovare un'altra strada :( Se il peso è minore o uguale al massimo, può passare, e il metodo restituisce true. Se non hai ancora compreso appieno le frasi "ritorno" o "il metodo restituisce un valore", prendiamo una pausa dalla programmazione e consideriamole utilizzando un semplice esempio tratto dalla vita reale. :) Diciamo che ti ammali e rimani a casa dal lavoro per alcuni giorni. Vai al reparto contabilità con la nota del medico, perché il congedo per malattia dovrebbe essere pagato. Se confrontiamo questa situazione con i metodi, allora il contabile ha un paySickLeave()metodo. Passi una nota del medico come argomento a questo metodo (senza di essa, il metodo non funzionerà e non verrai pagato!). Quindi i calcoli necessari vengono effettuati all'interno del metodo utilizzando la tua nota (il contabile lo utilizza per calcolare quanto l'azienda dovrebbe pagarti) e il risultato del tuo lavoro (una somma di denaro) ti viene restituito. Il nostro programma funziona in modo simile. Chiama un metodo, gli passa i dati e alla fine riceve un risultato. Ecco il metodo main() del nostro programma BridgeOfficer :

public static void main(String[] args) {
    Truck first = new Truck();
    first.weight = 10000;
    Truck second = new Truck();
    second.weight = 20000;

    BridgeOfficer officer = new BridgeOfficer(15000);
    System.out.println("Truck 1! Can I go, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck 2! And can I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
Creiamo due camion con carichi di 10.000 e 20.000. E il ponte dove lavora l'ufficiale ha un peso massimo di 15.000. Il programma chiama il metodo officer.checkTruck(first) . Il metodo calcola tutto e restituisce true , che il programma salva nella variabile booleana canFirstTruckGo . Ora puoi farci quello che vuoi (proprio come puoi fare con i soldi che ti ha dato il commercialista). Alla fine della giornata, il codice

boolean canFirstTruckGo = officer.checkTruck(first);
si riduce a

boolean canFirstTruckGo =  true;
Ecco un punto molto importante: l' istruzione return non restituisce solo il valore di ritorno del metodo, ma interrompe anche l'esecuzione del metodo! Qualsiasi codice che viene dopo l' istruzione return non verrà eseguito!

public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, you're overweight!");
    } else {
        return true;
        System.out.println("Everything looks good, go ahead!");
    }
}
I commenti dell'ufficiale non verranno visualizzati, perché il metodo ha già restituito un risultato ed è terminato! Il programma ritorna nel punto in cui è stato chiamato il metodo. Non devi stare attento a questo: il compilatore Java è abbastanza intelligente da generare un errore quando provi a scrivere codice dopo un'istruzione return .

Avengers: guerra dei parametri

Ci sono situazioni in cui vorremo diversi modi per chiamare un metodo. Perché non creare la nostra intelligenza artificiale? Amazon ha Alexa, Apple ha Siri, quindi perché non dovremmo averne uno? :) Nel film Iron Man, Tony Stark crea la sua incredibile intelligenza artificiale, Jarvis. Rendiamo omaggio a quel fantastico personaggio e chiamiamo la nostra IA in suo onore. :) La prima cosa che dobbiamo fare è insegnare a Jarvis a salutare le persone che entrano nella stanza (sarebbe strano se un intelletto così straordinario si rivelasse scortese).

public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Good evening, " + name + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
Uscita console:
Good evening, Tony Stark. How are you?
Molto bene! Jarvis è ora in grado di accogliere gli ospiti. Ovviamente, il più delle volte sarà il suo maestro, Tony Stark. Ma cosa succede se non viene da solo! Il nostro metodo sayHi() accetta solo un argomento. E quindi può salutare solo una persona che entra nella stanza e ignorerà l'altra. Non molto educato, non sei d'accordo? :/

Sovraccarico del metodo Java

In questo caso possiamo risolvere il problema semplicemente scrivendo 2 metodi con lo stesso nome, ma parametri diversi:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }
}
Questo è chiamato sovraccarico del metodo. Il sovraccarico del metodo consente al nostro programma di essere più flessibile e di adattarsi a vari modi di lavorare. Rivediamo come funziona:

public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ". How are you?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
Uscita console:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
Eccellente, entrambe le versioni hanno funzionato. :) Ma non abbiamo risolto il problema! E se ci sono tre ospiti? Potremmo, ovviamente, sovraccaricare nuovamente il metodo sayHi() , in modo che accetti tre nomi di ospiti. Ma potrebbero essercene 4 o 5. Fino all'infinito. Non c'è un modo migliore per insegnare a Jarvis a gestire un numero qualsiasi di nomi, senza sovraccaricare il metodo sayHi() un milione di volte? :/ Certo che c'è! Se non ci fosse, pensi che Java sarebbe il linguaggio di programmazione più popolare al mondo? ;)

public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ". How are you?");
    }
}
Quando ( String... names ) viene utilizzato come parametro, indica che una raccolta di stringhe verrà passata al metodo. Non dobbiamo specificare in anticipo quanti saranno, quindi ora il nostro metodo è molto più flessibile:

public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Good evening, " + name + ". How are you?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
Uscita console:
Good evening, Tony Stark. How are you?
Good evening, Captain America. How are you?
Good evening, Black Widow. How are you?
Good evening, Hulk. How are you?
Alcuni codici qui non ti saranno familiari, ma non preoccuparti. È semplice al suo interno: il metodo prende ogni nome a turno e saluta ogni ospite! Inoltre, funzionerà con qualsiasi numero di stringhe passate! Due, dieci, persino mille: il metodo funzionerà correttamente con qualsiasi numero di invitati. Molto più conveniente che sovraccaricare il metodo per tutte le possibilità, non credi? :) Ecco un altro punto importante: l'ordine degli argomenti è importante! Diciamo che il nostro metodo accetta una stringa e un numero:

public class Person {

    public static void sayYourAge(String greeting, int age) {
        System.out.println(greeting + " " + age);
    }

    public static void main(String[] args) {
        sayYourAge("My age is ", 33);
        sayYourAge(33, "My age is "); // Error!
    }
}
Se il metodo sayYourAge della classe Person accetta una stringa e un numero come input, il programma deve passarli in quell'ordine specifico! Se li passiamo in un ordine diverso, il compilatore genererà un errore e la persona non sarà in grado di dire la sua età. A proposito, anche i costruttori, di cui abbiamo parlato nell'ultima lezione, sono metodi! Puoi anche sovraccaricarli (cioè creare diversi costruttori con diversi set di parametri) e l'ordine degli argomenti passati è di fondamentale importanza anche per loro. Sono metodi reali! :)

Ancora una volta per quanto riguarda i parametri

Sì, scusa, non abbiamo ancora finito con loro. :) L'argomento che studieremo ora è molto importante. C'è una probabilità del 90% che ti verrà chiesto di questo in ogni futura intervista! Parliamo del passaggio di argomenti ai metodi. Considera un semplice esempio:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        currentYear = currentYear-10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What year is it?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("How about now?");
        System.out.println(currentYear);
    }
}
La macchina del tempo ha due metodi. Entrambi prendono come input il numero che rappresenta l'anno in corso e ne aumentano o diminuiscono il valore (a seconda che si voglia andare nel passato o nel futuro). Ma, come puoi vedere dall'output della console, il metodo non funziona! Uscita console:
What year is it?
2018
How about now?
2018
Abbiamo passato la variabile currentYear al metodo goToPast() , ma il suo valore non è cambiato. Eravamo nel 2018 e qui siamo rimasti. Ma perché? :/ Perché le primitive in Java vengono passate ai metodi per valore. Che cosa significa? Quando chiamiamo il metodo goToPast() e gli passiamo la variabile int currentYear (=2018) , il metodo non ottiene la variabile currentYear stessa, ma piuttosto una sua copia. Naturalmente, anche il valore di questa copia è 2018, ma qualsiasi modifica alla copia non influisce in alcun modo sulla nostra variabile currentYear originale ! Rendiamo il nostro codice più esplicito e osserviamo cosa succede con currentYear:

public class TimeMachine {

    public void goToFuture(int currentYear) {
        currentYear = currentYear+10;
    }

    public void goToPast(int currentYear) {
        System.out.println("The goToPast method has started running!");
        System.out.println("currentYear inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("currentYear inside the goToPast method (at the end) = " + currentYear);
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2018;

        System.out.println("What was the year when the program started?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("And what year is it now?");
        System.out.println(currentYear);
    }
}
Uscita console:
What was the year when the program started?
2018
The goToPast method has started running!
currentYear inside the goToPast method (at the beginning) = 2018
currentYear inside the goToPast method (at the end) = 2008
And what year is it now?
2018
Ciò mostra chiaramente che la variabile passata al metodo goToPast() è solo una copia di currentYear . E la modifica della copia non influisce sul valore "originale". "Passa per riferimento" significa l'esatto contrario. Esercitiamoci sui gatti! Voglio dire, vediamo come appare il passaggio per riferimento usando un esempio di gatto. :)

public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Ora, con l'aiuto della nostra macchina del tempo, invieremo Smudge , il primo gatto che viaggia nel tempo al mondo, nel passato e nel futuro! Modifichiamo la classe TimeMachine in modo che funzioni con gli oggetti Cat ;

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }    
}
Ora i metodi non cambiano solo il numero passato. Piuttosto, cambiano il campo di età di quello specifico gatto . Ricorderai che questo non ha funzionato per noi con i primitivi, perché il numero originale non è cambiato. Vediamo cosa succederà!

public static void main(String[] args) {

    TimeMachine timeMachine = new TimeMachine();
    Cat smudge = new Cat(5);

    System.out.println("How old was Smudge when the program started?");
    System.out.println(smudge.age);

    timeMachine.goToFuture(smudge);
    System.out.println("How about now?");
    System.out.println(smudge.age);

    System.out.println("Holy smokes! Smudge has aged 10 years! Back up quickly!");
    timeMachine.goToPast(smudge);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(smudge.age);
}
Uscita console:
How old was Smudge when the program started running?
5
How about now?
15
Holy smokes! Smudge has aged 10 years! Back up quickly!
Did it work? Have we returned the cat to its original age?
5
Oh! Ora il metodo ha fatto qualcosa di diverso: il nostro gatto è invecchiato drasticamente, ma poi è tornato giovane! :) Proviamo a capire perché. A differenza dell'esempio con le primitive, quando gli oggetti vengono passati a un metodo, vengono passati per riferimento. Un riferimento all'oggetto smudge originale è stato passato al metodo changeAge() . Quindi, quando modifichiamo smudge.age all'interno del metodo, stiamo facendo riferimento alla stessa area di memoria in cui è memorizzato il nostro oggetto. È un riferimento allo stesso Smudge che abbiamo creato inizialmente. Questo si chiama "passaggio per riferimento"! Tuttavia, non tutto con i riferimenti è così facile. :) Proviamo a cambiare il nostro esempio:

public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat = new Cat(cat.age);
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat = new Cat(cat.age);
        cat.age -= 10;
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        Cat smudge = new Cat(5);

        System.out.println("How old was Smudge when the program started?");
        System.out.println(smudge.age);

        timeMachine.goToFuture(smudge);
        System.out.println ("Smudge went to the future! Has his age changed?");
        System.out.println(smudge.age);

        System.out.println ("And if you try going back?");
        timeMachine.goToPast(smudge);
        System.out.println(smudge.age);
    }
}
Uscita console:
How old was Smudge when the program started running?
5
Smudge went to the future! Has his age changed?
5
And if you try going back?
5
Non funziona di nuovo! О_О Scopriamo cosa è successo. :) Ha tutto a che fare con i metodi goToPast / goToFuture e come funzionano i riferimenti. Ora, la tua attenzione, per favore! Questa è la cosa più importante da capire su come funzionano riferimenti e metodi. Il fatto è che, quando chiamiamo il metodo goToFuture(Cat cat) , viene passata una copia del riferimento all'oggetto cat, non il riferimento stesso. Quindi, quando passiamo un oggetto a un metodo, ci sono due riferimenti all'oggetto. Questo è molto importante per capire cosa sta succedendo. Questo è esattamente il motivo per cui l'età del gatto non è cambiata nel nostro ultimo esempio. Nell'esempio precedente, durante la modifica dell'età, abbiamo semplicemente preso il riferimento passato a goToFuture()metodo e lo ha utilizzato per trovare l'oggetto in memoria e modificarne l'età ( cat.age += 10 ). Ma ora, all'interno del metodo goToFuture() , stiamo creando un nuovo oggetto ( cat = new Cat(cat.age) ) e a questo oggetto viene assegnata la stessa copia di riferimento passata al metodo. Di conseguenza:
  • Il primo riferimento ( Cat smudge = new Cat (5) ) indica il gatto originale (con età 5)
  • Successivamente, quando abbiamo passato la variabile cat al metodo goToPast() e le abbiamo assegnato un nuovo oggetto, il riferimento è stato copiato.
E questo ci ha portato al risultato finale: due riferimenti che puntano a due oggetti diversi. Ma abbiamo cambiato solo l'età di uno di essi (quello creato all'interno del metodo).

cat.age += 10;
E, naturalmente, nel metodo main() possiamo vedere sulla console che l'età del gatto, smudge.age , non è cambiata. Dopotutto, lo sfumino è una variabile di riferimento che punta ancora al vecchio oggetto originale con età di 5 anni e non abbiamo fatto nulla con quell'oggetto. Tutti i nostri cambiamenti di età sono stati eseguiti sul nuovo oggetto. Quindi, risulta che gli oggetti vengono passati ai metodi per riferimento. Le copie degli oggetti non vengono mai create automaticamente. Se passi un oggetto gatto a un metodo e cambi la sua età, cambierai anche la sua età. Ma le variabili di riferimento vengono copiate quando si assegnano valori e/o si chiamano metodi! Ripetiamo qui ciò che abbiamo detto sul passaggio di primitive: "Quando chiamiamo il metodo changeInt() e passiamo l' intvariable x (=15) , il metodo non ottiene la variabile x stessa, ma piuttosto una sua copia. Pertanto, qualsiasi modifica apportata alla copia non influirà sul nostro originale xFinirò comunque per discutere più di una volta su come gli argomenti vengono passati in Java (anche tra sviluppatori esperti). Ma ora sai esattamente come funziona. Continuate così! :) Per rafforzare ciò che hai imparato, ti suggeriamo di guardare una lezione video dal nostro corso Java
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION