JavaRush /Blog Java /Random-ES /Métodos, sus parámetros, interacción y sobrecarga.

Métodos, sus parámetros, interacción y sobrecarga.

Publicado en el grupo Random-ES
¡Hola de nuevo! En la última conferencia, nos familiarizamos con las clases y los constructores, y aprendimos cómo crear los nuestros. Métodos, sus parámetros, interacción y sobrecarga - 1Hoy veremos más de cerca una parte tan integral de las clases como los métodos. Un método es un conjunto de comandos que le permiten realizar alguna operación en un programa. En otras palabras, un método es una función; algo que tu clase pueda hacer. En otros lenguajes de programación, los métodos a menudo se denominan "funciones", pero en Java la palabra "método" se ha vuelto más popular :) En la última conferencia, si recuerdas, creamos métodos simples para la clase Cat para que nuestros gatos pudieran maullar. y salta:
public class Cat {

    String name;
    int age;

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

    public void jump() {
        System.out.println("¡Saltando al galope!");
    }

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

        barsik.sayMeow();
        barsik.jump();
    }
}
sayMeow()y jump()son métodos de nuestra clase. El resultado de su trabajo es la salida a la consola:
Мяу!
Прыг-скок!
Nuestros métodos son bastante simples: simplemente imprimen texto en la consola. Pero en Java, los métodos tienen una tarea principal: deben realizar acciones sobre los datos de un objeto . Cambie el valor de los datos de un objeto, transfórmelo, envíelo a la consola o haga algo más con él. Nuestros métodos actuales no hacen nada con los datos del objeto Cat. Veamos un ejemplo más claro:
public class Truck {

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

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
Por ejemplo, tenemos una clase que representa un camión: Truck. El remolque de un camión tiene un largo, un ancho, un alto y un peso (esto será necesario más adelante). En el método, getVolume()realizamos cálculos: transformamos los datos de nuestro objeto en un número que indica el volumen (multiplicamos el largo, el ancho y el alto). Este es el número que será el resultado del método. Tenga en cuenta que en la descripción del método está escrito public int getVolume. Esto significa que el resultado de este método debe ser un número en el formulario int. Hemos calculado el resultado del método y ahora debemos devolverlo a nuestro programa que llamó al método. Para devolver el resultado de un método en Java se utiliza la palabra clave return.
return volume;

Parámetros del método

Los métodos pueden aceptar valores como entrada, que se denominan "parámetros del método". Nuestro método actual getVolume()en la clase Truckno acepta ningún parámetro, así que intentemos expandir el ejemplo con camiones. Creemos una nueva clase - BridgeOfficer. Un oficial de policía está de guardia en el puente y controla todos los camiones que pasan para asegurarse de que sus cargas no excedan el límite de peso permitido.
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;
        }
    }
}
El método checkTrucktoma un parámetro como entrada: un objeto de camión Trucky determina si el oficial permitirá que el camión entre al puente o no. La lógica dentro del método es bastante simple: si el peso del camión excede el máximo permitido, el método devuelve false. Tendrás que buscar otro camino :( Si el peso es menor o igual al máximo, puedes pasar y el método regresa true. Si aún no entiendes completamente las frases “retorno”, “el método devuelve un valor ” - tomemos un descanso de la programación y veamos esto usando un ejemplo simple de la vida del mundo real :) Digamos que te enfermaste y no estuviste en el trabajo durante varios días. Vienes al departamento de contabilidad con tu baja laboral, que debes pagar. Si hacemos una analogía con los métodos, entonces el contador tiene un método paySickLeave()(“pagar la baja por enfermedad”). A este método se le pasa un certificado de baja por enfermedad como parámetro (sin él, el método no funcionará y no se le pagará nada). Dentro del método de la hoja de trabajo, se realizan los cálculos necesarios (el contador lo utiliza para calcular cuánto debe pagarle la empresa) y se le devuelve el resultado del trabajo: una suma de dinero. El programa funciona de la misma manera. Llama a un método, pasa datos allí y finalmente recibe el resultado. Aquí está el método main()para nuestro programa 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("¡Camión número 1! ¿Puedo pasar, oficial?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("¡Camión número 2! ¿Puedo?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
Estamos creando dos camiones con cargas de 10 000 y 20 000. Al mismo tiempo, el peso máximo para el puente donde está de servicio el oficial es 15 000. El programa llamó al método officer.checkTruck(first), el método calculó todo y devolvió el resultado al programa. true, y el programa lo guardó en la variable boolean canFirstTruckGo. Ahora puede hacer lo que quiera con él (al igual que tú con el dinero que recibiste del contador). En definitiva el código
boolean canFirstTruckGo = officer.checkTruck(first);
se reduce a
boolean canFirstTruckGo = true;
Un punto muy importante: ¡el operador returnno sólo devuelve el resultado del método, sino que también finaliza su trabajo ! ¡Todo el código escrito después de la devolución no se ejecutará!
public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("¡Date la vuelta, gordo!");
    } else {
        return true;
        System.out.println("¡Muy bien, adelante!");
    }
}
Las frases que dice el oficial no se enviarán a la consola, porque el método ya arrojó un resultado y completó su trabajo. El programa volvió al punto donde se llamó al método. No tiene que preocuparse por esto: el compilador de Java es lo suficientemente inteligente como para generar un error si intenta escribir código después de return.

Vengadores: Guerra de opciones

Hay situaciones en las que nuestro programa requiere varias opciones sobre cómo funciona un método. ¿Por qué no creamos nuestra propia inteligencia artificial? Amazon tiene a Alexa, Yandex tiene a Alice, entonces ¿por qué somos peores? :) En la película sobre Iron Man, Tony Stark creó su propia inteligencia artificial excepcional: JARVIS. Rindamos homenaje al maravilloso personaje y nombremos nuestra IA en su honor :) Lo primero que debemos enseñarle a Jarvis es saludar a las personas que entran en la habitación (sería extraño que un intelecto tan grande resultara ser descortés).
public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Buenas noches, " + name + ", ¿Cómo estás?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
Salida de consola:
Добрый вечер, Тони Старк, Cómo ваши дела?
¡Excelente! Jarvis sabe saludar a quien entra. La mayoría de las veces, por supuesto, será su dueño, Tony Stark. ¡Pero puede que no venga solo! Y nuestro método sayHi()toma solo un argumento como entrada. Y, en consecuencia, sólo podrá saludar a uno de los que vengan e ignorará al otro. No muy educado, ¿de acuerdo? :/ En este caso, para solucionar el problema, simplemente podemos escribir 2 métodos en la clase con el mismo nombre, pero con diferentes parámetros:
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Buenas noches, " + firstGuest + ", ¿Cómo estás?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Buenas noches, " + firstGuest + ", " + secondGuest + ", ¿Cómo estás?");
    }
}
Esto se llama sobrecarga de métodos . La sobrecarga permite que nuestro programa sea más flexible y se adapte a diferentes opciones de trabajo. Veamos cómo funciona:
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Buenas noches, " + firstGuest + ", ¿Cómo estás?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Buenas noches, " + firstGuest + ", " + secondGuest + ", ¿Cómo estás?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Capitan America");
    }
}
Salida de consola:
Добрый вечер, Тони Старк, Cómo ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, Cómo ваши дела?
Genial, ambas opciones funcionaron :) ¡Sin embargo, no resolvimos el problema! ¿Qué pasa si hay tres invitados? Por supuesto, podemos sobrecargar el método nuevamente sayHi()para aceptar los nombres de tres invitados. Pero puede haber 4 o 5. Y así hasta el infinito. ¿Existe otra forma de enseñarle a Jarvis a trabajar con cualquier cantidad de nombres, sin un millón de sobrecargas de métodos sayHi()? :/ ¡Por supuesto que sí! De lo contrario, ¿Java sería el lenguaje de programación más popular del mundo? ;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Buenas noches, " + name + ", ¿Cómo estás?");
    }
}
El registro ( String...names) pasado como parámetro nos permite indicar que se pasa una determinada cantidad de cadenas al método. No especificamos de antemano cuántos debería haber, por lo que el funcionamiento de nuestro método ahora se vuelve mucho más flexible:
public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Buenas noches, " + name + ", ¿Cómo estás?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Capitan America", "Viuda negra", "Casco");
    }
}
Salida de consola:
Добрый вечер, Тони Старк, Cómo ваши дела?
Добрый вечер, Капитан Америка, Cómo ваши дела?
Добрый вечер, Черная Вдова, Cómo ваши дела?
Добрый вечер, Халк, Cómo ваши дела?
Parte del código aquí no le resulta familiar, pero no se preocupe. Su esencia es simple: ¡el método pasa por todos los nombres por turno y saluda a cada uno de los invitados! Además, ¡funcionará para cualquier número de líneas transferidas! Dos, diez, incluso mil: el método funcionará de forma fiable con cualquier número de invitados. Mucho más conveniente que hacer sobrecargas para todas las opciones posibles, ¿no estás de acuerdo? :) Otro punto importante: ¡el orden de los argumentos importa! Digamos que nuestro método toma una cadena y un número como entrada:
public class Man {

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

    public static void main(String[] args) {
        sayYourAge("Mi edad - ", 33);
        sayYourAge(33, "Mi edad - "); //¡error!
    }
}
Si un método sayYourAgede clase Mantoma una cadena y un número como entrada, ¡este es el orden en el que deben pasarse en el programa! Si los pasamos en un orden diferente, el compilador arrojará un error y la persona no podrá saber su edad. Por cierto, ¡los constructores que cubrimos en la última lección también son métodos! También se pueden sobrecargar (crear varios constructores con diferentes conjuntos de argumentos) y para ellos el orden de paso de los argumentos también es de fundamental importancia. ¡Métodos reales! :)

Y de nuevo sobre los parámetros.

Sí, sí, todavía no hemos terminado con ellos :) El tema que consideraremos ahora es muy importante. ¡Existe un 90% de posibilidades de que le pregunten sobre esto en todas sus futuras entrevistas! Hablaremos sobre pasar parámetros a métodos. Veamos un ejemplo sencillo:
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("¿Qué año es ahora?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("¿Y ahora?");
        System.out.println(currentYear);
    }
}
La máquina del tiempo tiene dos métodos. Ambos toman como entrada un número que representa el año actual y aumentan o disminuyen el valor (dependiendo de si queremos retroceder en el tiempo o en el futuro). Pero, como se puede ver en la salida de la consola, ¡el método no funcionó! Salida de consola:
Какой сейчас год?
2020
А сейчас?
2020
Pasamos una variable currentYearal método goToPast(), pero su valor no cambió. Como fue en 2020, sigue siéndolo. ¿Pero por qué? :/ Porque las primitivas en Java se pasan a los métodos por valor. ¿Qué significa? Cuando llamamos a un método goToPast()y pasamos nuestra variable allí int currentYear = 2020, no es la variable en sí la que ingresa al método currentYear, sino una copia de ella . El valor de esta copia, por supuesto, también es igual a 2020, ¡pero todos los cambios que ocurren en la copia no afectan de ninguna manera nuestra variable originalcurrentYear ! Hagamos nuestro código más detallado y veamos qué sucede con currentYear:
public class TimeMachine {

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

    public void goToPast(int currentYear) {
        System.out.println("¡El método goToPast ha comenzado!");
        System.out.println("El valor del año actual dentro del método irAlPasado (al principio) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("El valor del año actual dentro del método irAlPasado (al final) = " + currentYear);
    }

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

        System.out.println("¿Cuál es el año al comienzo del programa?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("¿Qué año es ahora?");
        System.out.println(currentYear);
    }
}
Salida de consola:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Cómoой год?
2020
Esto muestra claramente que la variable que se pasó al método goToPast()es sólo una copia currentYear. Y cambiar la copia no tuvo ningún efecto sobre el significado del "original". " Pasar por referencia " tiene exactamente el significado opuesto. ¡Practiquemos con gatos! Quiero decir, veamos cómo se ve pasar por enlace usando gatos como ejemplo :)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
Ahora, con la ayuda de nuestra máquina del tiempo, lanzaremos a Barsik, el primer gato viajero en el tiempo del mundo, al pasado y al futuro. Cambiemos la clase TimeMachinepara que la máquina pueda trabajar con objetos Cat;
public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
Los métodos ahora cambian no sólo el número pasado, sino también el campo agede un objeto específico Cat. En el caso de los primitivos, como recordarán, no lo logramos: el número original no cambió. ¡Veamos qué pasa aquí!
public static void main(String[] args) {

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

    System.out.println("¿Qué edad tiene Barsik al comienzo del programa?");
    System.out.println(barsik.age);

    timeMachine.goToFuture(barsik);
    System.out.println("¿Y ahora?");
    System.out.println(barsik.age);

    System.out.println("¡Primeros palos! ¡Barsik ha envejecido 10 años! ¡Regresa rápido!");
    timeMachine.goToPast(barsik);
    System.out.println("¿Funcionó? ¿Hemos devuelto al gato a su edad original?");
    System.out.println(barsik.age);
}
Salida de consola:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
¡Guau! Ahora el método funcionó de otra manera: ¡nuestro gato envejeció repentinamente y luego volvió a parecer más joven! :) Intentemos descubrir por qué. A diferencia del ejemplo con primitivas, en el caso de objetos se pasa al método una referencia al objeto. Se pasó una referencia a nuestro objeto original goToFuture(barsik)a los métodos . Por lo tanto, cuando cambiamos los métodos internos , accedemos al área de memoria donde está almacenado nuestro objeto. Este es un enlace al mismo Barsik que creamos al principio. ¡Esto se llama "pasar por referencia"! Sin embargo, con estos enlaces no todo es tan sencillo :) Intentemos cambiar nuestro ejemplo: 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("¿Qué edad tiene Barsik al comienzo del programa?");
        System.out.println(barsik.age);

        timeMachine.goToFuture(barsik);
        System.out.println("¡Barsik se fue al futuro! ¿Ha cambiado su edad?");
        System.out.println(barsik.age);

        System.out.println("¿Y si lo intentas en el pasado?");
        timeMachine.goToPast(barsik);
        System.out.println(barsik.age);
    }
}
Salida de consola:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
¡No vuelve a funcionar! O_O Averigüemos qué pasó :) Se trata de los métodos goToPast/ goToFuturey la mecánica de cómo funcionan los enlaces. ¡Ahora atención!Este punto es el más importante para comprender cómo funcionan los enlaces y los métodos. De hecho, cuando llamamos a un método, goToFuture(Cat cat)no se le pasa la referencia del objeto en sí cat, sino una copia de esta referencia. Es decir, cuando pasamos un objeto a un método, hay dos referencias a este objeto . Esto es muy importante para entender lo que está sucediendo. Después de todo, es por eso que nuestro último ejemplo no cambió la edad del gato. En el ejemplo anterior con el cambio de edad, simplemente tomamos la referencia pasada dentro del método goToFuture(), encontramos el objeto en la memoria usándolo y cambiamos su edad ( cat.age += 10). Ahora dentro del método goToFuture()creamos un nuevo objeto.
(cat = new Cat(cat.age)),
y se asigna este objeto al mismo enlace de copia que se pasó al método. Como resultado:
  • El primer enlace ( Cat barsik = new Cat(5)) apunta al gato original (con 5 años)
  • Después de pasar la variable catal método goToPast(Cat cat)y asignarla a un nuevo objeto, se copió la referencia.
Después de esto, tenemos la situación final: dos enlaces apuntan a dos objetos diferentes. Pero cambiamos la edad de sólo uno de ellos: el que creamos dentro del método.
cat.age += 10;
Y, naturalmente, cuando lo enviamos main()a la consola en el método barsik.age, vemos que su antigüedad no ha cambiado. Después de todo barsik, esta es una variable de referencia que todavía apunta al objeto antiguo y original con 5 años de edad, al que no le pasó nada. Todas nuestras manipulaciones con la edad se realizaron sobre un objeto nuevo. Por tanto, resulta que los objetos se pasan a métodos por referencia. Las copias de objetos nunca se crean automáticamente. Si pasó un objeto cat a un método y cambió su edad, cambiará exitosamente. ¡Pero los valores de las variables de referencia se copian al asignar y/o llamar a métodos! Repitamos aquí el párrafo sobre el paso de primitivas: "Cuando llamamos a un método changeInt()y pasamos nuestra variable allí int x = 15, no es la variable en sí la que ingresa al método x, sino su copia . Después de todo, todos los cambios que suceden en la copia no afectar nuestra variable original de alguna manera x”. Al copiar enlaces, ¡todo funciona exactamente igual! Pasas el objeto cat al método. Si haces algo con el gato (es decir, con el objeto en la memoria), todos los cambios se realizarán correctamente: solo teníamos un objeto y todavía lo tenemos. Pero si dentro de un método creas un nuevo objeto y lo guardas en una variable de referencia, que es un parámetro del método, de ahora en adelante tenemos dos objetos y dos variables de referencia. ¡Eso es todo! No fue tan fácil, incluso podrías tener que sermonear varias veces. Pero lo principal es que hayas aprendido este tema súper importante. A menudo encontrará argumentos (incluso entre desarrolladores experimentados) sobre cómo se pasan los argumentos en Java. Ahora sabes exactamente cómo funciona. ¡Avanza! :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION