JavaRush /Java Blog /Random-IT /Metodi, loro parametri, interazione e sovraccarico

Metodi, loro parametri, interazione e sovraccarico

Pubblicato nel gruppo Random-IT
Ciao di nuovo! Nell'ultima lezione abbiamo conosciuto classi e costruttori e abbiamo imparato a crearne di nostri. Metodi, loro parametri, interazione e sovraccarico - 1Oggi daremo uno sguardo più da vicino a una parte così integrante delle classi come i metodi. Un metodo è un insieme di comandi che consente di eseguire alcune operazioni in un programma. In altre parole, un metodo è una funzione; qualcosa che la tua classe può fare. In altri linguaggi di programmazione, i metodi sono spesso chiamati "funzioni", ma in Java la parola "metodo" è diventata più popolare :) Nell'ultima lezione, se ricordi, abbiamo creato semplici metodi per la classe Cat in modo che i nostri gatti potessero miagolare e salta:
public class Cat {

    String name;
    int age;

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

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

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

        barsik.sayMeow();
        barsik.jump();
    }
}
sayMeow()e jump()sono metodi della nostra classe. Il risultato del loro lavoro è l'output sulla console:
Мяу!
Прыг-скок!
I nostri metodi sono abbastanza semplici: stampano semplicemente il testo sulla console. Ma in Java i metodi hanno un compito principale: devono eseguire azioni sui dati di un oggetto . Cambia il valore dei dati di un oggetto, trasformalo, invialo alla console o fai qualcos'altro con esso. I nostri metodi attuali non fanno nulla con i dati dell'oggetto Cat. Consideriamo un esempio più chiaro:
public class Truck {

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

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
Ad esempio, abbiamo una classe che rappresenta un camion - Truck. Un rimorchio per camion ha una lunghezza, una larghezza, un'altezza e un peso (questo sarà necessario in seguito). Nel metodo, getVolume()eseguiamo calcoli: trasformiamo i dati del nostro oggetto in un numero che indica il volume (moltiplicamo lunghezza, larghezza e altezza). Questo è il numero che sarà il risultato del metodo. Nota: nella descrizione del metodo è scritto public int getVolume. Ciò significa che il risultato di questo metodo deve essere un numero nella forma int. Abbiamo calcolato il risultato del metodo e ora dobbiamo restituirlo al nostro programma che ha chiamato il metodo. Per restituire il risultato di un metodo in Java, viene utilizzata la parola chiave return.
return volume;

Parametri del metodo

I metodi possono accettare valori come input, chiamati "parametri del metodo". Il nostro metodo attuale getVolume()nella classe Trucknon accetta alcun parametro, quindi proviamo ad espandere l'esempio con i camion. Creiamo una nuova classe - BridgeOfficer. Un agente di polizia è in servizio sul ponte e controlla tutti i camion in transito per assicurarsi che i loro carichi non superino il limite di 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 checkTruckprende un parametro come input: un oggetto camion Trucke determina se l'ufficiale consentirà o meno al camion di salire sul ponte. La logica all'interno del metodo è abbastanza semplice: se il peso del camion supera il massimo consentito, il metodo restituisce false. Dovrai cercare un'altra strada :( Se il peso è inferiore o uguale al massimo, puoi passare e il metodo restituisce true. Se ancora non comprendi appieno le frasi "ritorno", "il metodo restituisce un valore ” - prendiamoci una pausa dalla programmazione e guardiamo questo utilizzando un semplice esempio tratto dalla vita reale :) Diciamo che ti sei ammalato e non sei stato al lavoro per diversi giorni. Vieni in contabilità con il tuo congedo per malattia, che devi pagare. Se tracciamo un'analogia con i metodi, allora il contabile ha un metodo paySickLeave()("pagare il congedo per malattia"). Passi un certificato di congedo per malattia a questo metodo come parametro (senza di esso il metodo non funzionerà e non ti verrà pagato nulla!). All'interno del metodo del foglio di lavoro vengono effettuati i calcoli necessari (il contabile lo utilizza per calcolare quanto dovrebbe pagarti l'azienda) e ti viene restituito il risultato del lavoro: una somma di denaro. Il programma funziona allo stesso modo. Chiama un metodo, passa i dati lì e infine riceve il risultato. Ecco il metodo main()per il 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 number 1! May I pass, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck number 2! May I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
Stiamo creando due camion con un carico di 10.000 e 20.000. Allo stesso tempo, il peso massimo per il ponte dove è in servizio l'ufficiale è di 15.000. Il programma ha chiamato il metodo officer.checkTruck(first), il metodo ha calcolato tutto e ha restituito il risultato al programma: truee il programma lo ha salvato nella variabile boolean canFirstTruckGo. Ora può farne quello che vuole (proprio come te con i soldi che hai ricevuto dal commercialista). In definitiva il codice
boolean canFirstTruckGo = officer.checkTruck(first);
si riduce a
boolean canFirstTruckGo = true;
Un punto molto importante: l'operatore returnnon solo restituisce il risultato del metodo, ma ne termina anche il lavoro ! Tutto il codice scritto dopo il reso non verrà eseguito!
public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, overweight!");
    } else {
        return true;
        System.out.println("Alright, move on!");
    }
}
Le frasi pronunciate dall'ufficiale non verranno inviate alla console, perché il metodo ha già restituito un risultato e ha completato il suo lavoro! Il programma è tornato al punto in cui è stato chiamato il metodo. Non devi preoccuparti di questo da solo: il compilatore Java è abbastanza intelligente da generare un errore se provi a scrivere codice dopo return.

Avengers: Guerra delle Opzioni

Ci sono situazioni in cui il nostro programma richiede diverse opzioni su come funziona un metodo. Perché non creiamo la nostra intelligenza artificiale? Amazon ha Alexa, Yandex ha Alice, quindi perché siamo peggio? :) Nel film su Iron Man, Tony Stark ha creato la sua eccezionale intelligenza artificiale - JARVIS Rendiamo omaggio al meraviglioso personaggio e chiamiamo la nostra IA in suo onore :) Il La prima cosa che dobbiamo insegnare a Jarvis è salutare le persone che entrano nella stanza (sarebbe strano se un così grande intelletto si rivelasse scortese).
public class Jarvis {

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
Uscita console:
Добрый вечер, Тони Старк, How ваши дела?
Grande! Jarvis sa salutare chi entra. Molto spesso, ovviamente, sarà il suo proprietario: Tony Stark. Ma potrebbe non venire da solo! E il nostro metodo sayHi()accetta solo un argomento come input. E, di conseguenza, potrà salutare solo uno di quelli che verranno e ignorerà l'altro. Non molto educato, d'accordo? :/ In questo caso, per risolvere il problema, possiamo semplicemente scrivere 2 metodi nella classe con lo stesso nome, ma con parametri diversi:
public class Jarvis {

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

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }
}
Questo è chiamato sovraccarico del metodo . Il sovraccarico consente al nostro programma di essere più flessibile e di adattarsi a diverse opzioni di lavoro. Controlliamo come funziona:
public class Jarvis {

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

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
Uscita console:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
Ottimo, entrambe le opzioni hanno funzionato :) Tuttavia, non abbiamo risolto il problema! E se gli ospiti fossero tre? Naturalmente possiamo sovraccaricare nuovamente il metodo sayHi()per accettare i nomi di tre ospiti. Ma possono essercene 4 o 5. E così via all'infinito. Esiste un altro modo per insegnare a Jarvis a lavorare con un numero qualsiasi di nomi, senza un milione di sovraccarichi di metodi sayHi()? :/ Certo che sì! Altrimenti 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 doing?");
    }
}
Il record ( String...names) passato come parametro permette di indicare che un certo numero di stringhe vengono passate al metodo. Non specifichiamo in anticipo quanti dovrebbero essercene, quindi il funzionamento del nostro metodo diventa ora molto più flessibile:
public class Jarvis {

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

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
Uscita console:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
Parte del codice qui non ti è familiare, ma non preoccuparti. La sua essenza è semplice: il metodo esamina a turno tutti i nomi e saluta ciascuno degli ospiti! Inoltre, funzionerà per qualsiasi numero di linee trasferite! Due, dieci, anche mille: il metodo funzionerà in modo affidabile con qualsiasi numero di ospiti. Molto più conveniente che sovraccaricare tutte le opzioni possibili, non sei d'accordo? :) Un altro punto importante: l'ordine degli argomenti è importante! Supponiamo che il nostro metodo prenda una stringa e un numero come input:
public class Man {

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

    public static void main(String[] args) {
        sayYourAge("My age - ", 33);
        sayYourAge(33, "My age - "); //error!
    }
}
Se un metodo sayYourAgedi classe Manaccetta una stringa e un numero come input, allora questo è l'ordine in cui devono essere passati al programma! Se li passiamo in un ordine diverso, il compilatore genererà un errore e la persona non sarà in grado di determinare la sua età. A proposito, anche i costruttori di cui abbiamo parlato nell'ultima lezione sono metodi! Possono anche essere sovraccaricati (creare più costruttori con diversi set di argomenti) e per loro anche l'ordine di passaggio degli argomenti è di fondamentale importanza. Metodi veri! :)

E ancora sui parametri

Sì, sì, non abbiamo ancora finito con loro :) L'argomento che considereremo ora è molto importante. C'è una probabilità del 90% che ti chiederanno questo in tutte le tue future interviste! Parleremo del passaggio dei parametri ai metodi. Diamo un'occhiata a 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 = 2020;

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

        timeMachine.goToPast(currentYear);
        System.out.println("And now?");
        System.out.println(currentYear);
    }
}
La macchina del tempo ha due metodi. Entrambi prendono come input un numero che rappresenta l'anno corrente e ne incrementano o decrementano il valore (a seconda se vogliamo tornare indietro nel tempo o nel futuro). Ma, come si può vedere dall’output della console, il metodo non ha funzionato! Uscita console:
Какой сейчас год?
2020
А сейчас?
2020
Abbiamo passato una variabile currentYearal metodo goToPast(), ma il suo valore non è cambiato. Come nel 2020, rimane così. Ma perché? :/ Perché le primitive in Java vengono passate ai metodi in base al valore. Cosa significa? Quando chiamiamo un metodo goToPast()e passiamo lì la nostra variabile int currentYear = 2020, non è la variabile stessa ad entrare nel metodo currentYear, ma una sua copia . Anche il valore di questa copia, ovviamente, è uguale a 2020, ma tutte le modifiche apportate alla copia non influenzano in alcun modo la nostra variabile originalecurrentYear ! Rendiamo il nostro codice più dettagliato e vediamo 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!");
        System.out.println("The currentYear value inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("The currentYear value inside the goToPast method (at the end) = " + currentYear);
    }

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

        System.out.println("What is the year at the very beginning of the program?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("What year is it now?");
        System.out.println(currentYear);
    }
}
Uscita console:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
Ciò mostra chiaramente che la variabile passata al metodo goToPast()è solo una copia currentYear. E cambiare la copia non ha avuto alcun effetto sul significato dell’“originale”. " Passando per riferimento " ha esattamente il significato opposto. Facciamo pratica sui gatti! Voglio dire, vediamo come appare il passaggio per collegamento usando i gatti come esempio :)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Ora, con l'aiuto della nostra macchina del tempo, lanceremo Barsik, il primo gatto che viaggia nel tempo, nel passato e nel futuro! Cambiamo la classe TimeMachinein modo che la macchina possa lavorare con gli oggetti Cat;
public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
I metodi ora modificano non solo il numero passato, ma il campo agedi un oggetto specifico Cat. Nel caso delle primitive, come ricorderete, non ci siamo riusciti: il numero originale non è cambiato. Vediamo cosa succede qui!
public static void main(String[] args) {

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

    System.out.println("How old is Barsik at the very beginning of the program?");
    System.out.println(barsik.age);

    timeMachine.goToFuture(barsik);
    System.out.println("And now?");
    System.out.println(barsik.age);

    System.out.println("Firs-sticks! Barsik has aged 10 years! Drive back quickly!");
    timeMachine.goToPast(barsik);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(barsik.age);
}
Uscita console:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
Oh! Ora il metodo funzionava diversamente: il nostro gatto improvvisamente invecchiava e poi sembrava di nuovo più giovane! :) Proviamo a capire perché. A differenza dell'esempio con le primitive, nel caso degli oggetti al metodo viene passato un riferimento all'oggetto. Un riferimento al nostro oggetto originale è stato passato goToFuture(barsik)ai metodi . Pertanto, quando cambiamo metodi interni , accediamo proprio all'area di memoria in cui è archiviato il nostro oggetto. Questo è un collegamento allo stesso Barsik che abbiamo creato all'inizio. Questo si chiama "passaggio per riferimento"! Tuttavia, con questi collegamenti non tutto è così semplice :) Proviamo a cambiare il nostro esempio: goToPast(barsik)barsikbarsik.age
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 barsik = new Cat(5);

        System.out.println("How old is Barsik at the very beginning of the program?");
        System.out.println(barsik.age);

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

        System.out.println("And if you try in the past?");
        timeMachine.goToPast(barsik);
        System.out.println(barsik.age);
    }
}
Uscita console:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
Non funziona più! O_O Scopriamo cosa è successo :) Riguarda i metodi goToPaste goToFuturei meccanismi di funzionamento dei collegamenti. Ora attenzione!Questo punto è il più importante per comprendere come funzionano i collegamenti e i metodi. Infatti, quando chiamiamo un metodo, goToFuture(Cat cat)non gli viene passato il riferimento all'oggetto stesso cat, ma una copia di questo riferimento. Cioè, quando passiamo un oggetto a un metodo, ci sono due riferimenti a questo oggetto . Questo è molto importante per capire cosa sta succedendo. Dopotutto, questo è il motivo per cui il nostro ultimo esempio non ha cambiato l’età del gatto. Nell'esempio precedente con la modifica dell'età, abbiamo semplicemente preso il riferimento passato all'interno del metodo goToFuture(), abbiamo trovato l'oggetto in memoria utilizzandolo e ne abbiamo modificato l'età ( cat.age += 10). Ora all'interno del metodo goToFuture()creiamo un nuovo oggetto
(cat = new Cat(cat.age)),
e a questo oggetto viene assegnato lo stesso collegamento di copia passato al metodo. Di conseguenza:
  • Il primo collegamento ( Cat barsik = new Cat(5)) punta al gatto originale (di età pari a 5 anni)
  • Dopo aver passato la variabile catal metodo goToPast(Cat cat)e averla assegnata a un nuovo oggetto, il riferimento è stato copiato.
Successivamente abbiamo la situazione finale: due collegamenti puntano a due oggetti diversi. Ma abbiamo cambiato l'età solo di uno di loro, quello creato all'interno del metodo.
cat.age += 10;
E naturalmente, quando lo trasmettiamo main()alla console nel metodo barsik.age, vediamo che la sua età non è cambiata. Dopotutto barsik, questa è una variabile di riferimento che punta ancora al vecchio oggetto originale con 5 anni, a cui non è successo nulla. Tutte le nostre manipolazioni con l'età sono state eseguite su un nuovo oggetto. Pertanto, risulta che gli oggetti vengono passati ai metodi per riferimento. Le copie degli oggetti non vengono mai create automaticamente. Se hai passato un oggetto gatto a un metodo e ne hai cambiato l'età, cambierà con successo. Ma i valori delle variabili di riferimento vengono copiati durante l'assegnazione e/o la chiamata dei metodi! Ripetiamo qui il paragrafo sul passaggio delle primitive: "Quando chiamiamo un metodo changeInt()e passiamo lì la nostra variabile int x = 15, non è la variabile stessa che entra nel metodo x, ma la sua copia . Dopotutto, tutti i cambiamenti che avvengono sulla copia non influenzare in alcun modo la nostra variabile originale x. Con la copia dei collegamenti, tutto funziona esattamente allo stesso modo! Passi l'oggetto cat al metodo. Se fai qualcosa con il gatto stesso (cioè con l'oggetto in memoria), tutte le modifiche verranno apportate con successo: avevamo un solo oggetto e ce l'abbiamo ancora. Ma se all'interno di un metodo creiamo un nuovo oggetto e lo salviamo in una variabile di riferimento, che è un parametro del metodo, da ora in poi avremo due oggetti e due variabili di riferimento. È tutto! Non è stato così facile, potresti anche dover tenere lezioni più volte. Ma la cosa principale è che hai imparato questo argomento estremamente importante. Incontrerai spesso discussioni (anche tra sviluppatori esperti) su come vengono passati gli argomenti in Java. Ora sai esattamente come funziona. Continuate così! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION