「こんにちは、アミーゴ! 今日のレッスンのトピックは、型変換の拡大と縮小です。プリミティブ型の拡大と縮小についてはずっと前に学びました。レベル 10 です。今日は、それが参照型に対してどのように機能するかについて話します。クラスのインスタンス。」

実際、すべては非常に簡単です。クラス、その親、親の親など、Object クラスに至るまでのクラスの継承チェーンを想像してください。クラスには、継承するクラスのすべてのメンバー メソッドが含まれるため、クラスのインスタンスは、その親の型である変数に保存できます。

以下に例を示します。

コード 説明
class Animal
{
public void doAnimalActions();
}class Cat extends Animal
{
public void doCatActions();
}class Tiger extends Cat
{
public void doTigerActions();
}
ここには、Animal、Cat、Tiger の 3 つのクラス宣言があります。猫は動物を継承します。そしてタイガーはキャットを引き継ぎます。
public static void main(String[] args)
{
Tiger tiger = new Tiger();
Cat cat = new Tiger();
Animal animal = new Tiger();
Object obj = new Tiger();
}
Tiger オブジェクトは、その祖先の型である変数に常に割り当てることができます。Tiger クラスの場合、これらは Cat、Animal、Object です。

次に、拡大コンバージョンと縮小コンバージョンを見てみましょう。

代入操作によって継承チェーンを上に (オブジェクト クラスに向かって) 移動させる場合は、拡大変換 (アップキャストとも呼ばれます) を扱っていることになります。オブジェクトの型に向かってチェーンを下に移動すると、それは縮小変換 (ダウンキャストとも呼ばれます) になります。

継承チェーンを上に移動することは、より一般的な型につながるため、拡張と呼ばれます。ただし、そうすることで、継承を通じてクラスに追加されたメソッドを呼び出すことができなくなります。

コード 説明
public static void main(String[] args)
{
Object obj = new Tiger();
Animal animal = (Animal) obj;
Cat cat = (Cat) obj;
Tiger tiger = (Tiger) animal;
Tiger tiger2 = (Tiger) cat;
}
型を絞り込む場合は、型変換演算子を使用する必要があります。つまり、明示的な変換を実行します。

これにより、Java マシンは、オブジェクトが変換先の型を本当に継承しているかどうかを確認します。

この小さな革新により、型キャスト エラーの数が大幅に減少し、Java プログラムの安定性が大幅に向上しました。

コード 説明
public static void main(String[] args)
{
Object obj = new Tiger();
if (obj instanceof Cat)
{
Cat cat = (Cat) obj;
cat.doCatActions();
}}
さらに良いのは、  instanceofチェックを使用することです
public static void main(String[] args)
{
Animal animal = new Tiger();
doAllAction(animal);

Animal animal2 = new Cat();
doAllAction(animal2);

Animal animal3 = new Animal();
doAllAction(animal3);
}

public static void doAllAction(Animal animal)
{
if (animal instanceof Tiger)
{
Tiger tiger = (Tiger) animal;
tiger.doTigerActions();
}

if (animal instanceof Cat)
{
Cat cat = (Cat) animal;
cat.doCatActions();
}

animal.doAnimalActions();
}
その理由は次のとおりです。左側の例を見てください。

私たち (コード) は、どのタイプのオブジェクトを扱っているかを常に知っているわけではありません。これは、変数と同じタイプ (Animal) または任意の子孫タイプ (Cat、Tiger) のオブジェクトである可能性があります。

doAllAction メソッドを考えてみましょう。渡されたオブジェクトの種類に関係なく、正しく動作します。

つまり、Animal、Cat、Tiger の 3 つのタイプすべてに対して正しく機能します。

public static void main(String[] args)
{
Cat cat = new Tiger();
Animal animal = cat;
Object obj = cat;
}
ここには 3 つの代入演算があります。これらはすべて、拡大コンバージョンの例です。

チェックは必要ないため、ここでは型キャスト演算子は必要ありません。 オブジェクト参照は、その祖先の 1 つを型とする変数に常に格納できます。

「ああ、最後から 2 番目の例ですべてが明確になりました。なぜチェックが必要なのか、そしてなぜ型キャストが必要なのかということです。」

「そう願っています。この事実に注目していただきたいのです。」

これによってオブジェクトに何らかの変化が生じることはありません。唯一変わるのは、特定の参照変数に対して呼び出すことができる

たとえば、Cat 変数を使用すると、doAnimalActions メソッドと doCatActions メソッドを呼び出すことができます。doTigerActions メソッドについては、たとえ Tiger オブジェクトを指していても、何も知りません。

「はい、分かりました。思ったより簡単でした。」