JavaRush /Java Blog /Random-JA /メソッド、そのパラメータ、相互作用、オーバーロード

メソッド、そのパラメータ、相互作用、オーバーロード

Random-JA グループに公開済み
またあったね!前回の講義では、クラスとコンストラクターについて学び、独自のクラスとコンストラクターを作成する方法を学びました。 メソッド、そのパラメータ、相互作用、およびオーバーロード - 1今日は、クラスの不可欠な部分であるメソッドについて詳しく見ていきます。 メソッドは、プログラム内で何らかの操作を実行できるようにする一連のコマンドです。言い換えれば、メソッドは関数です。あなたのクラスでできること。他のプログラミング言語ではメソッドは「関数」と呼ばれることが多いですが、Java では「メソッド」という言葉の方が一般的になっています :) 前回の講義では、猫がニャーと鳴くように Cat クラスの簡単なメソッドを作成しました。そしてジャンプします:
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()と はjump()私たちのクラスのメソッドです。彼らの作業の結果は、コンソールへの出力です。
Мяу!
Прыг-скок!
私たちのメソッドは非常に単純です。テキストをコンソールに出力するだけです。しかし、Java では、メソッドには主なタスクがあります。オブジェクトのデータに対してアクションを実行する必要があります。オブジェクトのデータの値を変更したり、変換したり、コンソールに出力したり、それに対して何らかの処理を実行します。現在のメソッドはオブジェクトのデータを何も処理しませんCat。より明確な例を見てみましょう。
public class Truck {

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

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
たとえば、トラックを表すクラスがあります - Truck。トラック トレーラーには長さ、幅、高さ、重量があります (これは後で必要になります)。このメソッドではgetVolume()計算を実行します。オブジェクトのデータを体積を示す数値に変換します (長さ、幅、高さを掛け合わせます)。これはメソッドの結果となる数値です。注意してください - 方法の説明にそのように書かれていますpublic int getVolume。これは、このメソッドの結果が の形式の数値でなければならないことintを意味します。メソッドの結果を計算しました。次に、その結​​果をメソッドを呼び出したプログラムに返す必要があります。Java でメソッドの結果を返すには、キーワードを使用しますreturn
return volume;

メソッドパラメータ

メソッドは、「メソッド パラメーター」と呼ばれる値を入力として受け入れることができます。getVolume()このクラスの現在のメソッドはTruckパラメーターを受け入れないため、トラックを使用して例を拡張してみます。新しいクラスを作成しましょう - BridgeOfficer。橋の上では警察官が常駐しており、通過するすべてのトラックをチェックして、荷物が許容重量制限を超えていないかどうかを確認します。
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;
        }
    }
}
このメソッドはcheckTruck入力として 1 つのパラメータ (トラック オブジェクト) を受け取りTruck、警察官がトラックの橋への進入を許可するかどうかを決定します。メソッド内のロジックは非常に単純です。トラックの重量が最大許容値を超えた場合、メソッドは を返しますfalse。別の道路を探す必要があります:( 重みが最大値以下の場合は通過でき、メソッドは戻ります。true「戻る」という表現がまだ完全に理解できない場合は、「メソッドは値を返します」 」 - プログラミングから離れて、現実世界の生活からの簡単な例を使用してこれを見てみましょう:) あなたが病気になり、数日間仕事を休んだとします。あなたは病気休暇を持って経理部に来ましたが、その休暇は支払わなければなりません。方法に例えると、会計士には方法paySickLeave()(「病気休暇の支払い」)があります。病気休暇証明書をパラメータとしてこのメ​​ソッドに渡します (それがないと、メソッドは機能せず、何も支払われません)。ワークシートメソッド内では、必要な計算が行われ(会計士はそれを使用して、会社があなたに支払うべき金額を計算します)、作業の結果、つまり金額が返されます。 プログラムも同様に動作します。メソッドを呼び出し、そこにデータを渡し、最後に結果を受け取ります。main()私たちのプログラムのメソッドは次のとおりです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);
}
積載量 10,000 と 20,000 の 2 台のトラックを作成しています。同時に、警察官が勤務する橋の最大重量は 15,000 です。プログラムはメソッドと呼ばれ、officer.checkTruck(first)メソッドはすべてを計算し、結果をプログラムに返します - true、プログラムはそれを変数に保存しましたboolean canFirstTruckGo。今、彼はそれを使って好きなことをすることができます(あなたが会計士から受け取ったお金を使うのと同じように)。最終的にはコード
boolean canFirstTruckGo = officer.checkTruck(first);
に帰着します
boolean canFirstTruckGo = true;
非常に重要な点: オペレーターはreturnメソッドの結果を返すだけでなく、その作業も終了します。return の後に書かれたコードはすべて実行されません。
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!");
    }
}
メソッドはすでに結果を返し、作業を完了しているため、役員が言ったフレーズはコンソールには出力されません。プログラムはメソッドが呼び出された時点に戻りました。これについて自分で心配する必要はありません。Java コンパイラは、 の後にコードを書こうとするとエラーをスローするほど賢いのでreturn

アベンジャーズ: オプション戦争

プログラムがメソッドの動作方法について複数のオプションを必要とする場合があります。 独自の人工知能を作成してみませんか? Amazon には Alexa があり、Yandex には Alice があるのに、なぜ私たちは劣っているのでしょうか? :) アイアンマンの映画で、トニー スタークは独自の優れた人工知能を作成しました - JARVIS この素晴らしいキャラクターに敬意を表し、彼に敬意を表して AI に名前を付けましょう :)私たちが最初にジャービスに教えなければならないことは、部屋に入ってくる人々に挨拶することです(そのような優れた知性が失礼であることが判明した場合は奇妙です)。
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");
    }
}
コンソール出力:
Добрый вечер, Тони Старк, How ваши дела?
素晴らしい!ジャービスは入ってきた人に挨拶する方法を知っています。もちろん、ほとんどの場合、それは彼の飼い主であるトニー・スタークでしょう。でも、彼は一人では来られないかもしれません!そして、私たちのメソッドはsayHi()入力として引数を 1 つだけ受け取ります。そして、それに応じて、彼は来た人の一人だけを挨拶することができ、もう一人は無視するでしょう。あまり礼儀正しくないと思いますか?:/ この場合、問題を解決するには、クラス内に同じ名前でパラメータが異なる 2 つのメソッドを記述するだけです。
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 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");
    }
}
コンソール出力:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
素晴らしいですね、どちらのオプションもうまくいきました :) しかし、問題は解決しませんでした。ゲストが3人だったらどうしますか?もちろん、メソッドを再度オーバーロードしてsayHi()3 人のゲストの名前を受け入れることができます。ただし、その数は 4 つまたは 5 つになる可能性があり、それが無限に続きます。100 万ものメソッドをオーバーロードせずに、任意の数の名前を扱えるように Jarvis に教える別の方法はありますかsayHi()? :/ もちろん持っています! そうでなければ、Java は世界で最も人気のあるプログラミング言語になるでしょうか? ;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }
}
パラメーターとして渡されるレコード ( String...names) を使用すると、特定の数の文字列がメソッドに渡されることを示すことができます。いくつあるべきかを事前に指定しないため、メソッドの操作がより柔軟になります。
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");
    }
}
コンソール出力:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
ここにあるコードには馴染みのないものもありますが、気にしないでください。その本質はシンプルです。この方法では、すべての名前を順番に確認し、各ゲストに挨拶します。しかも転送回線数に関係なく使えます!2 人、10 人、さらには 1,000 人のゲストでも、この方法はどのような人数でも確実に機能します。考えられるすべてのオプションに対してオーバーロードを行うよりもはるかに便利ですよね? :) もう 1 つの重要なポイント: 引数の順序が重要です。私たちのメソッドが文字列と数値を入力として受け取るとします。
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!
    }
}
sayYourAgeクラスメソッドが文字列と数値を入力として受け取る 場合Man、プログラム内ではこの順序で渡す必要があります。異なる順序で渡すと、コンパイラはエラーをスローし、その人は自分の年齢を判断できなくなります。ちなみに、前回の講義で取り上げたコンストラクターもメソッドです。また、オーバーロードすることもできます (さまざまな引数セットを持つ複数のコンストラクターを作成する) があり、その場合、引数を渡す順序も基本的に重要です。本物のメソッド!:)

そして再びパラメータについて

はい、はい、まだこれで終わりではありません :) これから検討するトピックは非常に重要です。 今後のすべての面接で、90% の確率でこのことについて質問されるでしょう。 パラメータをメソッドに渡すことについて説明します。簡単な例を見てみましょう。
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);
    }
}
タイムマシンには2つの方法があります。どちらも現在の年を表す数値を入力として受け取り、その値を増分または減分します (過去に戻りたいか未来に戻りたいかに応じて)。しかし、コンソール出力からわかるように、この方法は機能しませんでした。コンソール出力:
Какой сейчас год?
2020
А сейчас?
2020
変数をcurrentYearメソッドに渡しましたgoToPast()が、その値は変わりませんでした。2020年もそうでしたが、それは今も変わりません。しかし、なぜ?:/ Java のプリミティブは値によってメソッドに渡されるためです。それはどういう意味ですか?メソッドを呼び出してgoToPast()そこに変数を渡す場合int currentYear = 2020、メソッドに取り込まれるのは変数そのものではなくcurrentYear、そのコピーです。もちろん、このコピーの値も 2020 に等しくなりますが、コピーに加えられたすべての変更は、元の変数にはまったく影響しませんcurrentYear。コードをより冗長にして、次の場合に何が起こるかを見てみましょう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);
    }
}
コンソール出力:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
これは、メソッドに渡された変数がgoToPast()単なるコピーであることを明確に示していますcurrentYear。そして、コピーを変更しても、「オリジナル」の意味には何の影響もありませんでした。「参照渡し」はまったく逆の意味を持ちます。猫で練習してみよう!つまり、猫を例として使用して、リンクによる通過がどのように見えるかを見てみましょう:)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
さあ、タイムマシンの助けを借りて、世界初の猫のタイムトラベラー、バーシックを過去と未来に送り出します!TimeMachineマシンがオブジェクトを操作できるようにクラスを変更しましょうCat
public class TimeMachine {

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

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
メソッドは、渡された数値だけでなく、age特定のオブジェクトのフィールドも変更するようになりましたCat。プリミティブの場合は、ご存知のとおり、成功しませんでした。元の数値は変更されませんでした。ここで何が起こるか見てみましょう!
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);
}
コンソール出力:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
おお!さて、この方法は違った効果を発揮しました。うちの猫は突然老けたのに、また若返ったように見えたのです。:) その理由を考えてみましょう。プリミティブの例とは異なり、オブジェクトの場合は、オブジェクトへの参照がメソッドに渡されます。元のオブジェクトへの参照がメソッドgoToFuture(barsik)に渡されました。したがって、メソッド内で変更を行うと、オブジェクトが保存されているメモリ領域そのものにアクセスすることになります。これは、最初に作成したのと同じ Barsik へのリンクです。これを「参照渡し」といいます。ただし、これらのリンクでは、すべてがそれほど単純ではありません:) 例を変更してみましょう。 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);
    }
}
コンソール出力:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
また効かない!O_O 何が起こったのか理解しましょう :) それはすべてメソッドと、リンクがどのように機能するかの仕組みに関するものgoToPastですgoToFuture今注目!この点は、リンクとメソッドがどのように機能するかを理解する上で最も重要です。実際、メソッドを呼び出すと、goToFuture(Cat cat)メソッドに渡されるのはオブジェクト参照そのものではなくcat、この参照のコピーです。 つまり、オブジェクトをメソッドに渡すとき、このオブジェクトへの参照が 2 つ存在します。これは何が起こっているのかを理解するために非常に重要です。結局のところ、これが最後の例で猫の年齢を変更しなかった理由です。年齢を変更する前の例では、単にメソッド内で渡された参照を取得しgoToFuture()、それを使用してメモリ内のオブジェクトを見つけ、その年齢を変更しました ( cat.age += 10)。次に、メソッド内でgoToFuture()新しいオブジェクトを作成します
(cat = new Cat(cat.age)),
そして、メソッドに渡されたのと同じコピー リンクがこのオブジェクトに割り当てられます。結果として:
  • 最初のリンク ( Cat barsik = new Cat(5)) は元の猫 (5 歳) を指します。
  • 変数をcatメソッドに渡しgoToPast(Cat cat)、それを新しいオブジェクトに割り当てた後、参照がコピーされました。
この後、最終的な状況が得られます。2 つのリンクが 2 つの異なるオブジェクトを指しています。ただし、そのうちの 1 つ、つまりメソッド内で作成したものだけの年齢を変更しました。
cat.age += 10;
main()そして当然のことながら、メソッドでコンソールに 出力するとbarsik.age、その年齢が変わっていないことがわかります。結局のところbarsik、これは、何も起こらなかった、経過時間 5 の古い元のオブジェクトをまだ指している参照変数です。年齢に応じたすべての操作は新しいオブジェクトに対して実行されました。したがって、オブジェクトは参照によってメソッドに渡されることがわかります。オブジェクトのコピーが自動的に作成されることはありません。cat オブジェクトをメソッドに渡し、その年齢を変更すると、正常に変更されます。ただし、参照変数の値は、メソッドの割り当てや呼び出しの際にコピーされます。ここでプリミティブの受け渡しに関する段落を繰り返しましょう: 「メソッドを呼び出してchangeInt()そこに変数を渡すときint x = 15、メソッドに取り込まれるのは変数自体ではなくx、そのコピーです。結局のところ、コピーに発生する変更はすべて影響を受けません。元の変数に何らかの形で影響を与えますx。」 リンクをコピーすると、すべてがまったく同じように機能します。cat オブジェクトをメソッドに渡します。猫自体 (つまり、メモリ内のオブジェクト) で何かを行うと、すべての変更が正常に完了します。オブジェクトは 1 つだけでしたが、それはまだ残っています。しかし、メソッド内で新しいオブジェクトを作成し、それをメソッドのパラメータである参照変数に保存すると、今後は 2 つのオブジェクトと 2 つの参照変数が存在することになります。それだけです!それはそれほど簡単ではなく、数回講義する必要があるかもしれません。しかし、重要なことは、この非常に重要なトピックを学習したということです。Java で引数を渡す方法について (経験豊富な開発者の間でも) 議論に遭遇することがよくあります。これで、それがどのように機能するかが正確にわかりました。それを維持する!:)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION