こんにちは!以前の講義の 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);
}
}
上の例では、まさにそれを行いました。操作は完了しましたが、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!
}
}
もちろん、ここではエラーが発生します。クラスは相互に関連Cat
しDog
ておらず、クラス間の「コンバータ」は作成していません。これを実行できないのは論理的です。コンパイラには、これらのオブジェクトを相互に変換する方法がわかりません。オブジェクトが相互に接続されているかどうかは別の問題です。どうやって?まず、継承を使用します。継承を使用して小規模なクラス システムを作成してみましょう。動物を表す一般的なクラスを用意します。
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
、継承ツリーを下っていきます。コンパイラーが明示的な指示がない限りそのような操作をスキップしないのは論理的ですが、括弧内に型を指定するとすべてが機能します。 もっと興味深い別の例を見てみましょう。
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
。その後、新しいリンクが同じオブジェクトを指し始めましたAnimal
。animal
その後、への変換を行いますPet
。ところで、なぜこんなことをしたのでしょうか?前回は例外が発生しました。なぜなら、今回のオリジナルのオブジェクトはPet pet
!
Pet pet = new Pet();
前の例では、それはオブジェクトでしたAnimal
。
Pet pet = (Pet) new Animal();
子孫変数に祖先オブジェクトを割り当てることはできません。逆に言えば、それができるのです。
GO TO FULL VERSION