1概要

Java型システムは、2種類の型から構成されています。プリミティブと参照です。

プリミティブ変換については、リンク:/java-primitive-conversions[この記事]で説明しました。ここでは、Javaがどのように型を処理するのかを理解するために、参照のキャストに焦点を当てます。


2プリミティブと参照

基本的な変換と参照変数のキャストは似ているように見えるかもしれませんが、それらはかなりhttps://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.1[異なる概念]です。

どちらの場合も、あるタイプを別のタイプに「変えて」います。しかし、簡単に言うと、プリミティブ変数はその値を含み、プリミティブ変数の変換はその値の不可逆的な変更を意味します。

double myDouble = 1.1;
int myInt = (int) myDouble;

assertNotEquals(myDouble, myInt);

上記の例の変換後、

myInt

変数は

1

になっています。以前の値である

1.1

を元に戻すことはできません。

  • 参照変数は異なります。参照変数はオブジェクトのみを参照しますが、オブジェクト自体は含まれません。

また、参照変数をキャストしても、それが参照するオブジェクトには影響しませんが、このオブジェクトに別の方法でラベルを付けるだけで、それを使用する機会が拡大または縮小されます。アップキャストはこのオブジェクトで利用可能なメソッドとプロパティのリストを絞り込み、ダウンキャストはそれを拡張することができます。

参照は、オブジェクトへのリモートコントロールのようなものです。リモコンにはその種類に応じてボタンの数が増減し、オブジェクト自体もヒープに格納されます。キャストするときは、リモコンの種類を変更しますが、オブジェクト自体は変更しません。


3アップキャスティング

サブクラスからスーパークラスへのキャストはアップキャストと呼ばれます。

通常、アップキャストはコンパイラによって暗黙的に実行されます。

アップキャスティングは継承と密接に関係しています。これはJavaのもう1つのコアコンセプトです。より具体的な型を参照するには、参照変数を使用するのが一般的です。そしてこれを実行するたびに、暗黙のアップキャストが行われます。

アップキャストを実演するために

Animal

クラスを定義しましょう:

public class Animal {

    public void eat() {
       //...
    }
}

それでは、

Animal

を拡張しましょう。

public class Cat extends Animal {

    public void eat() {
        //...
    }

    public void meow() {
        //...
    }
}

これで、

Cat

クラスのオブジェクトを作成し、それを

Cat

型の参照変数に割り当てることができます。

Cat cat = new Cat();

そしてそれを

Animal

型の参照変数に代入することもできます。

Animal animal = cat;

上記の割り当てでは、暗黙のアップキャストが行われます。それを明示的に行うことができます。

animal = (Animal) cat;

しかし、継承ツリーを明示的にキャストする必要はありません。

cat



Animal

であり、エラーを表示しません。

この参照は、宣言された型の任意のサブタイプを参照できます。

アップキャストを使用して、

Cat

インスタンスに使用できるメソッドの数を制限しましたが、インスタンス自体は変更していません。

Catに固有のことは何もできません。

animal

変数で

meow()__を呼び出すことはできません。


Cat

オブジェクトは

Cat

オブジェクトのままですが、

meow()

を呼び出すとコンパイラエラーが発生します。

----//animal.meow(); The method meow() is undefined for the type Animal
----


meow()を呼び出すには、

animal__を棄却する必要があります。後でこれを行います。

しかし今、私たちは私たちに何を期待しているのかを説明します。アップキャストのおかげで、ポリモーフィズムを利用することができます。


3.1. 多型


Animal

の別のサブクラス、

Dog

クラスを定義しましょう。

public class Dog extends Animal {

    public void eat() {
        //...
    }
}

これで、すべての猫と犬を

animals

のように扱う

feed()

メソッドを定義できます。

public class AnimalFeeder {

    public void feed(List<Animal> animals) {
        animals.forEach(animal -> {
            animal.eat();
        });
    }
}


AnimalFeeder

がリストに含まれている

animal



Cat

または

Dog

)を気にしたくない場合

feed()

メソッドでは、それらはすべて

animals

です。

暗黙のアップキャストは、特定の型のオブジェクトを

animals

リストに追加すると発生します。

List<Animal> animals = new ArrayList<>();
animals.add(new Cat());
animals.add(new Dog());
new AnimalFeeder().feed(animals);

私たちは猫と犬を追加し、それらは暗黙のうちに

Animal

タイプにキャストされています。



Cat



Animal

で、各

Dog



Animal

です。それらは多相です。

ところで、各オブジェクトは少なくとも

Object

であるため、すべてのJavaオブジェクトは多態的です。

Animal

のインスタンスを

Object

typeの参照変数に代入すると、コンパイラは文句を言いません。

Object object = new Animal();

そのため、私たちが作成するすべてのJavaオブジェクトには、

toString()

のように、すでに

Object

固有のメソッドがあります。

インターフェイスへのアップキャストも一般的です。


Mew

インターフェースを作成し、それを

Cat

に実装させることができます。

public interface Mew {
    public void meow();
}

public class Cat extends Animal implements Mew {

    public void eat() {
        //...
    }

    public void meow() {
        //...
    }
}

これで、どの

Cat

オブジェクトも

Mew

にアップキャストできます。

Mew mew = new Cat();


Cat



Mew

です。アップキャストは正当で暗黙のうちに行われます。

したがって、

Cat



Mew



Animal



Object

、および

Cat

です。この例では、4つすべてのタイプの参照変数に割り当てることができます。


3.2. オーバーライド

上記の例では、

eat()

メソッドがオーバーライドされています。つまり、

eat()



Animal

タイプの変数に対して呼び出されますが、実際のオブジェクト(猫と犬)に対して呼び出されるメソッドによって処理が行われます。

public void feed(List<Animal> animals) {
    animals.forEach(animal -> {
        animal.eat();
    });
}

ログをクラスに追加すると、

Cat



Dog

のメソッドが呼び出されることがわかります。

web - 2018-02-15 22:48:49,354[main]INFO com.baeldung.casting.Cat - cat is eating
web - 2018-02-15 22:48:49,363[main]INFO com.baeldung.casting.Dog - dog is eating

  • 総括する:**

  • 参照変数は、そのオブジェクトが

変数と同じ型、またはサブタイプの場合
** アップキャストは暗黙のうちに行われます

  • すべてのJavaオブジェクトは多相であり、のオブジェクトとして扱うことができます。

アップキャストによるスーパータイプ


4ダウンキャスト


Cat

クラスでしか使用できないメソッドを呼び出すために

Animal

型の変数を使用したい場合はどうなりますか?ここに曇りが来ます。スーパークラスからサブクラスへのキャストです。

例を見てみましょう:

Animal animal = new Cat();


animal

変数は

Cat

のインスタンスを参照することを私たちは知っています。そして

animal

の上で

Cat



meow()

メソッドを呼び出したいです。しかし、コンパイラは

meow()

メソッドが

Animal

型には存在しないと訴えています。


meow()

を呼び出すには、

animal



Cat

にダウンキャストする必要があります。

((Cat) animal).meow();

内側の括弧とそれに含まれる型はキャスト演算子と呼ばれることがあります。コードをコンパイルするには、外部括弧も必要です。

前の

AnimalFeeder

の例を

meow()

メソッドで書き換えましょう。

public class AnimalFeeder {

    public void feed(List<Animal> animals) {
        animals.forEach(animal -> {
            animal.eat();
            if (animal instanceof Cat) {
                ((Cat) animal).meow();
            }
        });
    }
}

これで、

Cat

クラスで利用可能なすべてのメソッドにアクセスできるようになりました。ログを調べて、

meow()

が実際に呼び出されていることを確認します。

web - 2018-02-16 18:13:45,445[main]INFO com.baeldung.casting.Cat - cat is eating
web - 2018-02-16 18:13:45,454[main]INFO com.baeldung.casting.Cat - meow
web - 2018-02-16 18:13:45,455[main]INFO com.baeldung.casting.Dog - dog is eating

上記の例では、実際には

Cat

のインスタンスであるオブジェクトのみをダウンキャストしようとしていることに注意してください。これを行うには、演算子

instanceof

を使用します。


4.1.

instanceof

演算子

オブジェクトが特定の型に属しているかどうかを確認するために、ダウンキャストの前に

instanceof

演算子を使用することがよくあります。

if (animal instanceof Cat) {
    ((Cat) animal).meow();
}


4.2.

ClassCastException



instanceof

演算子を使って型をチェックしていなければ、コンパイラは文句を言いませんでした。しかし、実行時には例外があります。

これを実証するために、上記のコードから

instanceof

演算子を削除しましょう。

public void uncheckedFeed(List<Animal> animals) {
    animals.forEach(animal -> {
        animal.eat();
        ((Cat) animal).meow();
    });
}

このコードは問題なくコンパイルされます。しかし、実行しようとすると例外が発生します。


java.lang.ClassCastException:com.baeldung.casting.Dog



com.baeldung.casting.Cat

にキャストすることはできません

これは、

Dog

のインスタンスであるオブジェクトを

Cat

インスタンスに変換しようとしていることを意味します。

ダウンキャスト先の型が実際のオブジェクトの型と一致しない場合、

__

ClassCastExceptionは常に実行時にスローされます。

無関係な型にダウンキャストしようとすると、コンパイラはこれを許可しません。

Animal animal;
String s = (String) animal;

コンパイラは「AnimalからStringにキャストできません」と言います。

コードをコンパイルするには、両方の型が同じ継承ツリーに含まれている必要があります。

まとめると:

  • ダウンキャストは、特定のメンバーにアクセスするために必要です。

サブクラス
** キャスト演算子を使用してダウンキャストが行われます

  • オブジェクトを安全にダウンキャストするには

    instanceof

    演算子が必要です

  • 実際のオブジェクトが私達がダウンキャストしたタイプと一致しない場合

実行時に

ClassCastException

がスローされます


5

Cast()

メソッド


Class

のメソッドを使用してオブジェクトをキャストする別の方法があります。

public void whenDowncastToCatWithCastMethod__thenMeowIsCalled() {
    Animal animal = new Cat();
    if (Cat.class.isInstance(animal)) {
        Cat cat = Cat.class.cast(animal);
        cat.meow();
    }
}

上記の例では、キャストおよび

instanceof

演算子の代わりに

cast(

)および

isInstance()

メソッドが対応して使用されています。

一般的な型で

cast()

メソッドと

isInstance()

メソッドを使用するのが一般的です。

typeパラメータの値に応じて、猫または犬の1種類の動物のみを「フィード」する

feed()メソッドを使用して

AnimalFeederGeneric <T> __クラスを作成しましょう。

public class AnimalFeederGeneric<T> {
    private Class<T> type;

    public AnimalFeederGeneric(Class<T> type) {
        this.type = type;
    }

    public List<T> feed(List<Animal> animals) {
        List<T> list = new ArrayList<T>();
        animals.forEach(animal -> {
            if (type.isInstance(animal)) {
                T objAsType = type.cast(animal);
                list.add(objAsType);
            }
        });
        return list;
    }

}


feed()

メソッドは各動物をチェックし、

T

のインスタンスであるものだけを返します。

型パラメータ

T

から取得できないため、

Class

インスタンスもジェネリッククラスに渡す必要があります。この例では、コンストラクタに渡します。


T



Cat

と等しくし、メソッドがcatだけを返すようにしましょう:

@Test
public void whenParameterCat__thenOnlyCatsFed() {
    List<Animal> animals = new ArrayList<>();
    animals.add(new Cat());
    animals.add(new Dog());
    AnimalFeederGeneric<Cat> catFeeder
      = new AnimalFeederGeneric<Cat>(Cat.class);
    List<Cat> fedAnimals = catFeeder.feed(animals);

    assertTrue(fedAnimals.size() == 1);
    assertTrue(fedAnimals.get(0) instanceof Cat);
}


6. 結論

この基本的なチュートリアルでは、アップキャスト、ダウンキャスト、それらの使用方法、およびこれらの概念がポリモーフィズムの活用にどのように役立つかについて説明しました。

いつものように、この記事のコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-lang-oop[over on GitHub]から入手できます。