CodeGym /Java Blog /ランダム /参照型の拡大と縮小
John Squirrels
レベル 41
San Francisco

参照型の拡大と縮小

ランダム グループに公開済み
やあ!過去のレッスンでは、プリミティブ型のキャストについて説明しました。何が議論されたかを簡単に思い出してみましょう。 参照タイプの拡大と縮小 - 1私たちは、プリミティブ型 (この場合は数値型) を、占有するメモリ量に応じてサイズが変化する入れ子人形として想像しました。覚えていると思いますが、大きな人形の中に小さな人形を入れることは、実生活でも Java プログラミングでも簡単です。

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
これは自動変換または拡張 の例です。これは自動的に行われるため、追加のコードを記述する必要はありません。結局のところ、私たちは何も変わったことはしていません。ただ小さな人形を大きな人形の中に入れているだけです。逆に、大きなロシアの人形を小さな人形の中に入れようとする場合は、別の問題になります。現実ではそんなことはできませんが、プログラミングならできます。しかし、ニュアンスが 1 つあります。intを変数に代入しようとしてもshort、物事はそれほどスムーズには進みません。結局のところ、short変数は 16 ビットの情報しか保持できませんが、a はint32 ビットを占有します。その結果、渡された値が歪んでしまいます。コンパイラはエラーを返します (「おい、何か疑わしいことをしている!」)')。ただし、値の変換先の型を明示的に指定すると、処理が続行されて実行されます。

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
それが上の例で行ったことです。操作は実行されましたが、short変数は 32 バイトのうち 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? 読み続ける前に、自分の答えを正当化するようにしてください。そして結果がこれです! アイムペット どうしてそうなったの?どれもシンプルです。親変数と子孫オブジェクトがあります。書くことで、

Animal animal = new Pet();
参照を拡張しPetて変数に割り当てまし たAnimal。プリミティブ型と同様に、参照型も Java では自動的に拡張されます。これを実現するために追加のコードを記述する必要はありません。これで、子孫オブジェクトが親参照に割り当てられました。その結果、メソッド呼び出しが子孫クラスで行われていることがわかります。このコードがなぜ機能するのかまだ完全に理解できない場合は、わかりやすい言葉で書き直してください。

Animal animal = new DomesticatedAnimal();
これで問題ないですよね?これが現実であり、参照は単に「動物」と書かれた紙のラベルであると想像してください。その紙をペットの首輪に取り付ければ、すべてが正しくなります。結局のところ、どんなペットも動物です!逆のプロセス (継承ツリーを子孫に移動する) では、絞り込みが行われます。

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!
   }
}
コンパイラがエラーを生成します。しかし、なぜ? 親オブジェクトを子孫参照に割り当てようとしているためです。 言い換えれば、次のようなことをしようとしています。

DomesticatedAnimal domesticatedAnimal = new Animal();
そうですね、変換先の型を明示的に指定すれば、おそらくすべてうまくいくでしょうか? これは数字でうまくいきました — 試してみましょう! :)

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変数はペット (およびその子孫) の保存にのみ適しており、あらゆる種類の動物の保存には適していません。ClassCastExceptionこのため、クラスのキャスト中にエラーが発生した場合に備えて、特別な Java 例外 が作成されました。内容を明確にするためにもう一度見直してみましょう。親参照は、子孫クラスのインスタンスを指すことができます。

public class Main {

   public static void main(String[] args) {

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

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
たとえば、ここでは問題はありません。Pet変数によって参照されるオブジェクトがありますPet。その後、Animal参照がまったく同じオブジェクトを指しました。その後、animalに変換します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