본문 바로가기

Java

상속 : 타입 변환과 다형성

다형성은 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 사용할 수 있도록 하는 성질이다.

도형(Figure)라는 클래스를 정의해보자

class Figure{
	double getArea() {
		return 0.0;
	}
	public String toString() {
		return "이것은 도형입니다.";
	}
}

Figure를 상속하는 Sagak 클래스

class Sagak extends Figure {
	int width, height;
	double area;
	
	public Sagak() {
		this.width = 1;
		this.height = 1;
		area = getArea();
		//this(1, 1); 로 쓸 수 있다.
	}
	public Sagak(int width, int height) {
		this.width = width;
		this.height = height;
		area = getArea();
	}
	public double getArea() {
		return width*height;
	}
	public String toString() {
		return "이 도형은 가로가 " + width + 
				" 이고 세로가 " + height + 
				" 이고 넓이가 " + area + 
				" 인 사각형입니다.";
	}
}

Figure를 상속하는 Samgak 클래스

class Samgak extends Figure {
	int width, height;
	double area;
	
	public Samgak() {
		this(1, 1);
		
	}
	public Samgak(int width, int height) {
		this.width = width;
		this.height = height;
		area = getArea();
	}
	public double getArea() {
		return width*height;
	}
	public String toString() {
		return "이 도형은 가로가 " + width + 
				" 이고 세로가 " + height + 
				" 이고 넓이가 " + area + 
				" 인 삼각형입니다.";
	}
}

Figure를 상속하는 Dongle 클래스

class Dongle extends Figure {
	int rad;
	double arround, area;
	
	// 생성자 함수
	public Dongle() {
		this(1);
	}
	
	public Dongle(int rad) {
		this.rad = rad;
		area = getArea();
		setArround();
	}
	
	public double getArea() {
		return rad * rad * 3.14;
	}
	
	// 둘레 변수 초기화 해주는 함수
	public void setArround() {
		arround = ((int)(2 * rad * 3.14 * 100 + 0.5)) / 100.0;
	}
	
	public String toString() {
		return "이 도형은 반지름이 " + rad + 
				" 이고  둘레가 " + arround + 
				" 이고 넓이가 " + area + 
				" 인 원입니다.";
	}
}

자동형변환

상위 클래스의 특징과 기능을 상속 받기 떄문에 상위 클래스와 동일하게 취급할 수 있다.

데이터 타입을 생각했을 때 객체 생성은 다음처럼 한다.

Figure f1 = new Figure();
Sagak f2 = new Sagak(15, 30);
Samgak f3 = new Samgak();
Dongle d1 =new Dongle();

하지만 이런식으로도 가능하다. 자동형변환이 일어난다.

Figure f1 = new Figure();
Figure f2 = new Sagak(15, 30);
Figure f3 = new Samgak();
Figure d1 = new Dongle();

상위 클래스가 갖고 있는 걸(메소드, 필드) 1~5라고 했을 때 하위 클래스는 1~6, 1~7, 1~10...을 갖게 된다. 1~5는 무조건 갖고 있기 때문에 형 변환이 가능하다.

... 변수할 때 형 변환과 조금 다른 것처럼 느껴진다.

하지만 이런 식으로는 안 된다. 서로 아무런 연관이 없는 클래스이다. (즉 상속 관계가 아니다)

Sagak s3 = new Samgak();
Dongle d3 = new Sagak();

필드와 메소드 접근

상위 클래스 타입으로 자동 형변환 되며 상위 클래스에 선언 된 필드와 메소드만 접근이 된다. 하지만 하위클래스에 오버라이딩 되었다면 하위 클래스의 메소드가 호출 된다.

class Figure{
	void method1(){..}
	void method2(){..}
}
class Samgak extends Figure{
	void method2() {...} //Override
	void method3() {...}
}
class SamgakExample{
	public static void main(String[] args){
		Figure figure = new Samgak();

		figure.method1(); //ok
		figure.method2();
		//오버라이딩 된 Samgak의 method2가 불러와짐
		figure.method3(); //사용할 수 없음
	}
}

자동형변환은 Samgak 객체가 Figure에 꾸-깃 꾸-깃 담긴거라고 생각하면 이해가 쉬울 듯 하다

Samgak클래스에 method1()이 상속되었기 때문에 사용할 수 있고,

method2()는 오버라이드 되어서 새로 정의된 게 Figure클래스에 덮어쓰기 되어 사용할 수 있다.

method3()은? (쑤셔서라도) 담을 공간이 없어서 그냥 사용 할 수 없게 된다.

자동 형 변환 왜 써?

그냥 쓰면 될 걸 왜 상위 클래스 타입으로 변환해서 사용하는걸까? 그건 다형성을 구현하는 기술적 방법 때문이다. 다형성이란.. 동일한 타입을 사용하지만 다양한 결과가 나오는 성질을 말한다.

상위 클래스를 상속하면... 하위 클래스가 상위 클래스의 필드와 메소드를 가지고 있으니 사용방법이 동일할 것이다. 바꿔서 사용하기 편리함!

버스가 있다고 해보자.

이 버스를 만들 때 바퀴의 지름은 10, 색깔은 검정, 재질은 고무로 만들어진 게 꼭 맞도록 설계했다. (상위 클래스에서 해당 내용을 정의한다)

근데 나는 왼쪽은 별 타이어, 오른쪽은 12각 형 타이어를 쓰고 싶다. 그러면 지름 10, 색깔 검정, 재질 고무인 타이어를 만들어서 별을 바른 타이어를 만들고, 지름 10정도 되는, 색깔 검정, 재질 고무인 12각 형 타이어를 만들면 된다. 그리고 쓰면 됨. (상위 클래스를 상속해서 사용한다)

이렇게 했을 때 기본적인 규칙만 지키기 때문에 어떤 타이어도 원하는 대로 만들어 쓸 수 있다.

이건 자동 형 변환이 되기 때문에 가능하다. 만약 자동 형 변환이 되지 않는다면? 일단 바퀴 종류마다 똑같은 코드가 들어가고 (코드 반복, 만약 바퀴가 1000종류라면? 코드 중복x1000)

바퀴 자리엔 "바퀴 모양이면 돼!"가 아니라, "별 타이어 전용이야!"로 설정되어 있어서 12각 형 타이어를 넣을 수 없음. 바꾸고 싶다? 설정을 다시 "12각 형 타이어 전용이야!"로 바꾸고, 12각 형 타이어를 넣어야 함. 타이어를 바꿀 때 마다 이 작업을 해줘야 하는 불상사가 생김

*배열은 같은 타입의 데이터 타입이 들어갈 수 있다. 타이어들 타입이 같으니 한 배열에 담을 수 있다.

매개 변수의 다형성

Driver 클래스

public class Vehicle{
	public void run(){
		System.out.println("차량이 달립니다");
	}
}

Vehicle 클래스를 이용하는 Driver 클래스

public class Driver{
	public void drive(Vehicle vehicle){
		vehicle.run();
	}
}

Vehicle 상속 받아서 run() 메소드 오버라이딩

Bus

public class Bus extends Vehicle{
	@Override
	public void run(
		System.out.println("버스가 달립니다");
	)
}

Taxi

public class Taxi extends Vehicle{
	@Override
	public void run(
		System.out.println("택시가 달립니다");
	)
}

이걸 사용하는 클래스를 보자

public class DriverExample{
	public static void main(String[] args){
		Driver driver = new Driver();
	
		Bus bus = new Bus();
		Taxi taxi = new Taxi();

		driver.drive(bus); //*
		driver.drive(taxi); //*
	}
}

//* vehicle전달 받아야 하는데 bus가 vehicle을 상속했으므로 자동 형 변환이 일어난다

객체 타입 확인? instanceof

그렇다면 vehicle 넣어야 하는 곳에 bus나 taxi 넣을 수 있단 걸 어떻게 알까? 물론 클래스를 하나하나 뜯어보면 알 수도 있을 테고, 클래스 명도 알 수 있게 지으려고 하겠지만..

클래스를 뜯어볼 수 없다면? 헷갈린다면? 그 때 쓸 수 있는 게 instanceof이다.

좌항(객체) instanceof 우항(타입)

boolean result = bus instanceof Vehicle //-> true
boolean result = taxi instanceof Vehicle //-> true
boolean result = bus instanceof Taxi //->false
반응형

'Java' 카테고리의 다른 글

static과 싱글톤  (0) 2021.03.11
상속 : 추상 클래스  (0) 2021.03.07
상속 : Protected 접근 제한자  (0) 2021.03.07
상속 : final 클래스와 final 메소드  (0) 2021.03.07
상속 : 메소드 재정의(오버라이딩)  (0) 2021.03.07