JavaRush /Java Blog /Random-JA /参照型の拡張と縮小

参照型の拡張と縮小

Random-JA グループに公開済み
こんにちは!以前の講義の 1 つで、プリミティブ型のキャストについて説明しました。私たちが話していたことを簡単に思い出してみましょう。 参照型の拡張と縮小 - 1プリミティブ型 (この場合は数値) を、占有するメモリ量に応じて入れ子人形として表現しました。覚えているように、小さな入れ子人形を大きな入れ子人形の中に配置することは、実生活でも Java プログラミングでも簡単です。
public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
これは自動変換または拡張子 の例です。これは自動的に行われるため、追加のコードを記述する必要はありません。結局のところ、私たちは何も変わったことはしていません。単に小さな入れ子人形を大きな入れ子人形の中に入れているだけです。逆に、大きなマトリョーシカ人形を小さなマトリョーシカ人形の中に入れようとする場合は、別の問題になります。これは人生では不可能ですが、プログラミングでは可能です。ただし、注意点が 1 つあります。int変数に値を入れようとしてもshort、そう簡単にはうまくいきません。結局のところ、変数に収まる情報は 16 ビットだけですshortが、値intには 32 ビット必要です。その結果、送信される値に歪みが生じます。コンパイラはエラー (「おい、何か怪しいことをしている! 」)を返しますが、値をキャストする型を明示的に指定した場合でも、そのような操作は実行されます。
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
上の例では、まさにそれを行いました。操作は完了しましたが、short32 ビットのうち 16 ビットのみが変数に適合したため、最終値が歪められ、その結果、数値-27008が返されました。この操作は、明示的な変換または縮小と呼ばれます。

参照型の拡張と縮小の例

次に、同じ操作について説明しますが、プリミティブ型ではなく、オブジェクトと参照変数に適用できます。これは Java ではどのように機能するのでしょうか? 実際には非常にシンプルです。互いに関連性のないオブジェクトがあります。これらは明示的にも自動的にも相互に変換できないと仮定するのが論理的です。
public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog();//error!

   }

}
もちろん、ここではエラーが発生します。クラスは相互に関連CatDogておらず、クラス間の「コンバータ」は作成していません。これを実行できないのは論理的です。コンパイラには、これらのオブジェクトを相互に変換する方法がわかりません。オブジェクトが相互に接続されているかどうかは別の問題です。どうやって?まず、継承を使用します。継承を使用して小規模なクラス システムを作成してみましょう。動物を表す一般的なクラスを用意します。
public class Animal {

   public void introduce() {

       System.out.println("i'm Animal");
   }
}
ご存知のとおり、動物には家畜と野生があります。
public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("i'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("i'm Pet");
   }
}
たとえば、飼い犬とコヨーテの犬を考えてみましょう。
public class Dog extends Pet {

   public void introduce() {

       System.out.println("i'm Dog");
   }
}





public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println("i'm Coyote");
   }
}
私たちのクラスは、認識しやすくするために、意図的に最も原始的なものになっています。ここでは実際にはフィールドは必要なく、1 つのメソッドで十分です。次のコードを実行してみましょう。
public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
コンソールには何が出力されると思いますか? introduceクラスPetまたはクラスメソッドは機能しますかAnimal? 読み続ける前に、自分の答えを正当化するようにしてください。そして結果がこれです! i'm Pet なぜこの答えになったのですか?それは簡単です。親変数と子オブジェクトがあります。書くことで:
Animal animal = new Pet();
参照型を拡張しPet、そのオブジェクトを変数に格納しまし たAnimal。プリミティブ型と同様に、Java の参照型の拡張は自動的に行われます。このために追加のコードを記述する必要はありません。これで、子オブジェクトが親参照にアタッチされ、その結果、メソッドが子クラスで呼び出されることがわかります。このコードがなぜ機能するのかまだ完全に理解できない場合は、簡単な言語で書き直してください。
Животное животное = new ДомашнееЖивотное();
それは問題ないですよね?これが現実であり、この場合のリンクは「動物」と書かれた単純な紙のタグであると想像してください。このような紙をペットの首輪に取り付ければ、すべてがうまくいきます。どんなペットでも動物です!逆のプロセス、つまり相続ツリーを相続人まで下位に移動することは、絞り込みです。
public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
ご覧のとおり、ここではオブジェクトをどのクラスにキャストするかを明示的に示しています。以前は変数 がありましたWildAnimalが、現在はCoyote、継承ツリーを下っていきます。コンパイラーが明示的な指示がない限りそのような操作をスキップしないのは論理的ですが、括弧内に型を指定するとすべてが機能します。 参照型の拡張と縮小 - 2 もっと興味深い別の例を見てみましょう。
public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
コンパイラがエラーをスローします。理由は何ですか? 実際には、親オブジェクトを子変数に割り当てようとしています。 言い換えれば、これをやりたいのです:
ДомашнееЖивотное домашнееЖивотное = new Животное();
しかし、キャスト先の型を明示的に指定すれば成功するでしょうか? 数字はうまくいきそうなので、試してみましょう! :)
public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
スレッド「メイン」java.lang.ClassCastException での例外: 動物をペットエラーにキャストできません。今回はコンパイラはエラーを出しませんでしたが、結果として例外を受け取りました。理由はすでにわかっています。親オブジェクトを子変数に割り当てようとしているからです。実際、なぜこれができないのでしょうか? すべての動物がペットではないからです。 オブジェクトを作成しAnimal、それを変数に割り当てようとしていますPet。しかし、たとえば、コヨーテも家畜ではありますが、家畜でAnimalはありません。Petつまり、次のように書くと、
Pet pet = (Pet) new Animal();
new Animal()どんな動物でも そこにいることができ、家畜である必要はありません。当然のことながら、変数はPet petペット (およびその子孫) の保存にのみ適しており、すべての人に適しているわけではありません。したがって、そのような場合のために、Java ではClassCastExceptionクラスをキャストするときのエラーという特別な例外が作成されました。わかりやすくするためにもう一度言ってみましょう。親変数 (参照) は、子孫クラスのオブジェクトを指すことができます。
public class Main {

   public static void main(String[] args) {

       Pet pet =  new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
たとえば、ここでは何も問題はありません。Petリンクによって指されているオブジェクトがありますPet。その後、新しいリンクが同じオブジェクトを指し始めましたAnimalanimalその後、への変換を行いますPet。ところで、なぜこんなことをしたのでしょうか?前回は例外が発生しました。なぜなら、今回のオリジナルのオブジェクトはPet pet!
Pet pet =  new Pet();
前の例では、それはオブジェクトでしたAnimal
Pet pet = (Pet) new Animal();
子孫変数に祖先オブジェクトを割り当てることはできません。逆に言えば、それができるのです。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION