자바에서 객체 유형 캐스팅

1. 개요

Java 유형 시스템은 기본 유형과 참조 유형의 두 가지 유형으로 구성됩니다.

이 기사에서 원시 변환을 다루었으며 여기서는 참조 캐스팅에 중점을 두어 Java가 유형을 처리하는 방법을 잘 이해할 것입니다.

2. 원시 대 참조

기본 변환과 참조 변수 캐스팅은 비슷해 보일 수 있지만 개념은 상당히 다릅니다.

두 경우 모두 한 유형을 다른 유형으로 "전환"합니다. 그러나 단순화 된 방식으로 기본 변수는 그 값을 포함하고 기본 변수의 변환은 값의 돌이킬 수없는 변경을 의미합니다.

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

위의 예에서 변환 한 후 myInt 변수는 1 이며 이전 값 1.1 을 복원 할 수 없습니다 .

참조 변수는 다릅니다 . 참조 변수는 개체를 참조 할뿐 개체 자체는 포함하지 않습니다.

참조 변수를 캐스팅하는 것은 참조하는 개체를 건드리지 않고 다른 방식으로이 개체에 레이블을 지정하여 작업 할 기회를 확대하거나 축소합니다. 업 캐스팅은이 개체에 사용할 수있는 메서드 및 속성 목록을 좁히고 다운 캐스팅을 통해 확장 할 수 있습니다.

참조는 객체에 대한 원격 제어와 같습니다. 리모콘에는 유형에 따라 더 많거나 적은 버튼이 있으며 개체 자체는 힙에 저장됩니다. 캐스팅을 할 때 리모컨의 종류는 변경하지만 개체 자체는 변경하지 않습니다.

3. 업 캐스팅

서브 클래스에서 슈퍼 클래스로 캐스팅하는 것을 업 캐스팅이라고 합니다. 일반적으로 업 캐스팅은 컴파일러에 의해 암시 적으로 수행됩니다.

업 캐스팅은 Java의 또 다른 핵심 개념 인 상속과 밀접한 관련이 있습니다. 보다 구체적인 유형을 참조하기 위해 참조 변수를 사용하는 것이 일반적입니다. 그리고 우리가 이것을 할 때마다 암시 적 업 캐스팅이 발생합니다.

업 캐스팅을 보여주기 위해 Animal 클래스를 정의 해 보겠습니다 .

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

이제 Animal을 확장 해 보겠습니다 .

public class Cat extends Animal { public void eat() { // ... } public void meow() { // ... } }

이제 우리의 객체 생성 할 수 있습니다 고양이 클래스와 타입의 참조 변수에 할당 고양이 :

Cat cat = new Cat();

또한 Animal 유형의 참조 변수에 할당 할 수도 있습니다 .

Animal animal = cat;

위의 할당에서 암시 적 업 캐스팅이 발생합니다. 명시 적으로 할 수 있습니다.

animal = (Animal) cat;

그러나 상속 트리를 명시 적으로 캐스팅 할 필요는 없습니다. 컴파일러는 catAnimal 임을 알고 오류를 표시하지 않습니다.

참조는 선언 된 유형의 모든 하위 유형을 참조 할 수 있습니다.

업 캐스팅을 사용하여 Cat 인스턴스에 사용할 수있는 메서드 수를 제한 했지만 인스턴스 자체는 변경하지 않았습니다. 이제 우리는 Cat에 특정한 어떤 것도 할 수 없습니다 . 동물 변수 에 대해 meow () 를 호출 할 수 없습니다 .

하지만 고양이의 개체가 남아 고양이 호출 객체를 야옹 () 컴파일러 오류가 발생합니다 :

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

meow () 를 호출하려면 animal 을 다운 캐스트해야합니다 . 나중에이 작업을 수행합니다.

그러나 이제 우리는 우리에게 업 캐스팅을 제공하는 것을 설명 할 것입니다. 업 캐스팅 덕분에 우리는 다형성을 활용할 수 있습니다.

3.1. 다형성

하자가의 또 다른 서브 클래스 정의 동물 하는 클래스를 :

public class Dog extends Animal { public void eat() { // ... } }

이제 모든 고양이와 개를 동물 처럼 취급 하는 feed () 메서드를 정의 할 수 있습니다 .

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

AnimalFeeder 가 목록 에있는 동물 , 즉 고양이 또는 에 대해 신경 쓰지 않기를 바랍니다 . 에서 공급 () 방법들은 모두 동물 .

암시 적 업 캐스팅은 특정 유형의 개체를 동물 목록에 추가 할 때 발생 합니다.

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

우리는 고양이와 개를 추가하고 암묵적 으로 동물 유형 으로 업 캐스트됩니다 . 각 고양이동물 이고 각 동물 입니다. 그들은 다형성입니다.

각 개체가 있기 때문에 그건 그렇고, 모든 Java 객체는 다형성 객체 적어도. 개체 유형 의 참조 변수 에 Animal 인스턴스를 할당 할 수 있으며 컴파일러는 불평하지 않습니다.

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();

CatMew 이고 upcasting은 합법적이며 암시 적으로 수행됩니다.

따라서 CatMew , Animal , ObjectCat 입니다. 이 예제에서는 네 가지 유형 모두의 참조 변수에 할당 할 수 있습니다.

3.2. 재정의

위의 예에서는 eat () 메서드가 재정의되었습니다. 즉, eat ()Animal 유형 의 변수에서 호출 되지만 작업은 고양이와 개와 같은 실제 개체에서 호출 된 메서드에 의해 수행됩니다.

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

클래스에 로깅을 추가하면 CatDog 메서드가 호출되는 것을 볼 수 있습니다.

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. 다운 캐스팅

Animal 유형의 변수를 사용하여 Cat 클래스 에만 사용할 수있는 메서드를 호출하려면 어떻게해야합니까? 여기에 다운 캐스팅이 있습니다. 슈퍼 클래스에서 서브 클래스로 캐스팅하는 것입니다.

예를 들어 보겠습니다.

Animal animal = new Cat();

We know that animal variable refers to the instance of Cat. And we want to invoke Cat’s meow() method on the animal. But the compiler complains that meow() method doesn’t exist for the type Animal.

To call meow() we should downcast animal to Cat:

((Cat) animal).meow();

The inner parentheses and the type they contain are sometimes called the cast operator. Note that external parentheses are also needed to compile the code.

Let’s rewrite the previous AnimalFeeder example with meow() method:

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

Now we gain access to all methods available to Cat class. Look at the log to make sure that meow() is actually called:

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

Note that in the above example we're trying to downcast only those objects which are really instances of Cat. To do this, we use the operator instanceof.

4.1. instanceof Operator

We often use instanceof operator before downcasting to check if the object belongs to the specific type:

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

4.2. ClassCastException

If we hadn't checked the type with the instanceof operator, the compiler wouldn't have complained. But at runtime, there would be an exception.

To demonstrate this let’s remove the instanceof operator from the above code:

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

This code compiles without issues. But if we try to run it we’ll see an exception:

java.lang.ClassCastException: com.baeldung.casting.Dog cannot be cast to com.baeldung.casting.Cat

This means that we are trying to convert an object which is an instance of Dog into a Cat instance.

ClassCastException's always thrown at runtime if the type we downcast to doesn't match the type of the real object.

Note, that if we try to downcast to an unrelated type, the compiler won't allow this:

Animal animal; String s = (String) animal;

The compiler says “Cannot cast from Animal to String”.

For the code to compile, both types should be in the same inheritance tree.

Let's sum up:

  • Downcasting is necessary to gain access to members specific to subclass
  • Downcasting is done using cast operator
  • To downcast an object safely, we need instanceof operator
  • If the real object doesn't match the type we downcast to, then ClassCastException will be thrown at runtime

5. cast() Method

There's another way to cast objects using the methods of Class:

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

In the above example, cast() and isInstance() methods are used instead of cast and instanceof operators correspondingly.

It's common to use cast() and isInstance() methods with generic types.

Let's create AnimalFeederGeneric class with feed() method which “feeds” only one type of animals – cats or dogs, depending on the value of the type parameter:

public class AnimalFeederGeneric { private Class type; public AnimalFeederGeneric(Class type) { this.type = type; } public List feed(List animals) { List list = new ArrayList(); animals.forEach(animal -> { if (type.isInstance(animal)) { T objAsType = type.cast(animal); list.add(objAsType); } }); return list; } }

The feed() method checks each animal and returns only those which are instances of T.

Note, that the Class instance should also be passed to the generic class as we can't get it from the type parameter T. In our example, we pass it in the constructor.

Let's make T equal to Cat and make sure that the method returns only cats:

@Test public void whenParameterCat_thenOnlyCatsFed() { List animals = new ArrayList(); animals.add(new Cat()); animals.add(new Dog()); AnimalFeederGeneric catFeeder = new AnimalFeederGeneric(Cat.class); List fedAnimals = catFeeder.feed(animals); assertTrue(fedAnimals.size() == 1); assertTrue(fedAnimals.get(0) instanceof Cat); }

6. Conclusion

이 기초 자습서에서는 업 캐스팅, 다운 캐스팅이 무엇인지, 사용 방법 및 이러한 개념이 다형성을 활용하는 데 어떻게 도움이되는지 살펴 보았습니다.

항상 그렇듯이이 기사의 코드는 GitHub에서 사용할 수 있습니다.