💡 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다.
CaptionTv() c = new CaptionTv();
Tv t = new CaptionTv(); //조상 타입의 참조변수로 자손 인스턴스를 참조
둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
public class Test {
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println(p.x);
p.method();
System.out.println(c.x);
c.method();
}
}
class Parent {
int x = 100;
void method() {
System.out.println("Parent Method");
}
}
class Child extends Parent {
int x = 200;
void method() {
System.out.println("Child Method");
}
}
💡 하지만, 멤버 변수들은 주로
private
으로 접근을 제한하고, 외부에서는 메서드를 통해서만 멤버변수에 접근할 수 있도록 한다. 대부분 다른 외부 클래스에서 참조변수를 통해 직접적으로 인스턴스 변수에 접근할 수 있게 하지 않는다.
인스턴스 변수에 직접 접근하면, 참조변수의 타입에 따라 사용되는 인스턴스변수가 달라질 수 있으므로 주의해야 한다.
class Shape {
Shape() {
System.out.println("도형입니다.");
}
public void draw () {
System.out.println("도형을 그립니다.");
}
}
class Rectangle extends Shape {
Rectangle () {
System.out.println("사각형 생성자");
}
@Override
public void draw() {
System.out.println("사각형을 그립니다.");
}
public void draw2() {
System.out.println("사각형을 그립니다.2222");
}
}
public class PolyTest {
public static void main(String[] args) {
//1번
Shape shape = new Shape();
shape.draw();
//2번
Rectangle rec = new Rectangle();
rec.draw();
Shape rec2 = new Rectangle();
//에러
//rec2.draw2();
}
}
Shape 타입으로 선언하면 훨씬 넓은 범위의 객체를 받을 수 있다.
public static void printArea (Shape shape) {
System.out.println (shape.getArea());
}
💡 같은 함수로 인스턴스마다 다른 동작을 하여 유연성이 생기지만, 부모에게서 상속된 멤버만 사용하므로 임의로 만든 멤버는 사용을 제외시킬 수 있어서 일관성을 얻을 수 있다.
기본형 변수와 같이 참조변수도 형변환이 가능하다. 단, 서로 상속 관계에 있는 클래스 사이에서만 가능하다.
바로 윗 조상이나 자손이 아닌, 조상의 조상으로도 형변환이 가능하다. 따라서 모든 참조변수는 모든 클래스의 조상인 Object
클래스 타입으로 형변환이 가능하다.
기본형에서 작은 자료형에서 큰 자료형은 형변환은 생략이 가능하듯이, 참조형 변수의 형변환에서는 자손타입의 참조변수를 조상타입으로 형변환하는 경우에는 형변환을 생략할 수 있다.
- 자손타입 → 조상타입 (Up-casting) : 형변환 생략가능
Parent p = new Child();
- 조상타입 → 자손타입 (Down-casting) : 형변환 생략불가
Child c = new Parent(); //불가능
형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.
단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조절하는 것 뿐이다.
참조하고 있는 인스턴스가 자손 클래스의 인스턴스일 경우 다운캐스팅이 가능하다.
Parent p = new Child();
Child c = (Child) p; //다운캐스팅 가능
Child c = new Parent(); //불가능
자손타입으로 형변환은 생략할 수 없으며, 형변환을 수행하기 전에 instanceof 연산자를 사용해서 참조변수가 참조하고 있는 실제 인스턴스 타입을 확인하는 것이 안전하다.
class Parent { void print() { System.out.println("Parent 메서드 호출"); }}
class Child extends Parent {
@Override
void print () { System.out.println("Child 메서드 호출"); }
}
public class Casting {
public static void main(String[] args) {
Parent p = new Child(); //업캐스팅 : 자식 객체를 부모 객체로 형변환
p.print(); //동적 메소드 호출 : 자식의 print() 호출
//Child c = new Parent();
Child c = (Child) p; //다운캐스팅 : 부모 객체를 자식 객체로 형변환
p.print(); //메서드 오버라이딩, 자식 객체의 print() 호출
}
}
//result
Child 메서드 호출
Child 메서드 호출
💡 객체가 해당 인스턴스를 가지고 있느냐
참조변수 instanceof 데이터타입(클래스명)
if (shape instanceof Rectangle) {
Rectangle rec = (Rectangle) shape;
/**
캐스팅이 가능하면 자손의 캐스팅 후 필요한 멤버를 사용한다.
**/
}
//참조형 변수 r은 ture 출력
boolean a = r instanceof Object;
System.out.println(a);
public class PolyTest {
public static void print (Shape shape) {
if (shape instanceof Rectangle) {
System.out.println("실제 타입은 Rectangle 입니다.");
} else if (shape instanceof Circle) {
System.out.println("실제 타입은 Circle입니다.");
} else {
System.out.println("알수 없는 타입니다.");
}
}
public static void main(String[] args) {
Shape[] shape = { new Triangle(10, 10), new Rectangle(10, 10), new Circle (10) };
for (Shape s : shape) {
print(s);
}
}
}
//result
알수 없는 타입니다.
실제 타입은 Rectangle 입니다.
실제 타입은 Circle입니다.