넓은 범위의 타입 변수에 좁은 타입의 값이 대입되면 자동 타입 변환이 일어난다.
byte small = 10;
int big = small;
// small 의 type 이 byte 로 자동 변환됨
마찬가지로 넓은 객체 타입 변수에 좁은 객체가 대입되면,
좁은 객체의 타입은 넓은 객체 타입으로 자동 변환된다.
이때 넓은 범위 객체는 조상 클래스, 좁은 범위 객체는 자손 클래스이다.
부모타입 변수 = 자식타입객체;
Child child = new Child();
Parent parent = child;
// child 변수에 저장된 Child 객체의 타입이 Parent로 변환된다.
Animal animal = new Cat();
// Cat 객체가 생성되고 타입이 Animal 로 변환된다.
child 변수와 parent 변수는 타입이 다르지만, 동일한 Child 객체를 참조하고 있다. (cat == animal)
자동 타입 변환된 변수는 자손 객체의 주소를 저장한다.
따라서 메서드 호출 시, 자손 객체에서 오버라이딩 한 버전으로 호출된다.
하지만 타입이 조상객체 타입이므로, 부모 클래스에 있는 멤버에만 접근할 수 있다.
class Parent {
void method1() { ... }
void method2() { ... }
}
class Child {
void method2() { ... } // overide
void method3() { ... }
}
class Example {
public static void main (String[] args) {
Parent parent = new Child();
parent.method1(); // 호출 O
parent.method2(); // 호출 O (overide 버전)
parent.method3(); // 호출 X (parent 객체의 멤버가 아니므로)
}
}
좁은 타입 변수에 넓은 객체를 대입할 때에는 캐스팅 연산자를 사용해서 타입을 강제로 변환한다.
자식 객체를 부모 타입으로 자동 변환한 후, 다시 자식 타입으로 변환하고 싶을 때에만 강제 타입 변환이 가능하다.
자식타입 변수 = (자식타입) 부모타입객체;
Parent parent = new Child(); // 부모타입으로 자동변환
Child child = (Child) parent; // 자식타입으로 강제 변환
자식 객체를 부모 타입으로 자동 변환했기 때문에, 자식 객체에만 있는 메서드는 사용하지 못했다.
하지만 자식 객체에만 있는 메서드를 꼭 사용해야 한다면, 강제 타입 변환을 이용해 자식 타입으로 다시 변환해야한다.
사용 방법은 같은데 실행 결과는 다르게 나오는 성질
부모 객체의 자식 객체인 객체 A와 객체 B가 있을 때, 자식 객체들이 부모 객체의 메서드를 오버라이딩 했다면, 실행 결과는 다르게 나올 수 있다.
/* CPU.java */
public class CPU {
public void powerOn() {
System.out.println("CPU 전원 ON");
}
}
/* Intel.java */
public class Intel extends CPU {
public void powerOn() {
System.out.println("Intel CPU 전원 ON");
}
}
/* AMD.java */
public class AMD extends CPU {
public void powerOn() {
System.out.println("AMD CPU 전원 ON");
}
}
외부의 Desktop 객체에서 CPU 객체를 생성하고, CPU 객체의 powerOn() 메서드를 사용한다.
/* Desktop.java : CPU 객체 사용 */
public class Desktop {
/* cpu 필드 선언 */
public CPU cpu;
/* start 메서드 선언 */
public void start() {
cpu.powerOn();
}
}
cpu 필드 : Inter , AMD 객체가 대입된다.
start 메서드 : cpu 필드에 대입된 각 객체마다 오버라이딩 된 powerOn 메서드를 가지고 있을 수 있다. 이때 실행 결과는 달라질 수 있다.
/* Example.java : 실행하기 */
public class Example {
public static void main(String[] args) {
Desktop pc1 = new Desktop();
/* CPU 객체 생성 */
pc1.cpu = new CPU();
pc1.start(); // CPU 전원 ON
/* Intel 객체 생성 */
pc1.cpu = new Intel();
pc1.start(); // Intel CPU 전원 ON
/* AMD 객체 생성 */
pc1.cpu = new AMD();
pc1.start(); // AMD CPU 전원 ON
}
}
메서드가 클래스 타입의 매개변수를 가지고 있는 경우가 있다.
위 Desktop.java 파일에서, CPU 객체 타입의 필드를 선언하고, 이 객체의 메서드인 powerOn() 을 실행시켜주는 메서드 start 를 작성했다.
하지만 아래와 같이 필드를 따로 선언하지 않고, CPU 타입 객체를 매개변수로 받을 수 있다.
public class Desktop {
public void start(CPU cpu) {
cpu.powerOn();
}
}
start 메서드를 호출할 때, CPU 객체를 매개변수로 전달하고싶다면 생성해서 전달하면 된다.
Desktop pc1 = new Desktop();
CPU cpu = new CPU();
pc1.start(cpu);
하지만 CPU 의 자식 객체인 Intel , AMD 객체를 매개변수로 전달하고 싶다면, 자동 타입 변환을 활용해야 한다. 매개변수 타입은 CPU 로 지정되어 있기 때문이다.
매개변수에 자식 객체를 전달하는 과정에서 자동 타입 변환이 일어나게 된다.
/* Example.java : 실행하기 */
public class Example {
public static void main(String[] args) {
Desktop pc1 = new Desktop();
Intel intel = new Intel();
pc1.start(intel); // 타입 변환
AMD amd = new AMD();
pc1.start(amd); // 타입 변환
}
}
변수나 매개변수가 참조하는 객체의 타입을 확인하고 싶다면 instanseof 연산자를 사용한다.
boolean result = 객체변수 instanseof 객체타입
매개변수로 받은 객체가 자식 타입에서 부모타입으로 자동 변환 된건지 확인하고, 다시 자식타입으로 강제 변환 해주고 싶을 경우,
void method(Parent parent) {
if(parent instanceof Child) {
Child child = (Child) parent;
// child 변수 사용
}
}
위와 같이 변환할 수도 있고, 우측 타입 변수를 사용할 수 있다.
void method(Parent parent) {
if(parent instanceof Child child) {
// child 변수 사용
}
}
좋은 글이네요. 공유해주셔서 감사합니다.