14. 업 캐스팅(Up-Casting)과 다운 캐스팅(Down-Casting) [ JAVA ]

duck-ach·2022년 8월 2일
0

JAVA

목록 보기
14/27

캐스팅(Casting)

캐스팅(Casting) 이란, 타입을 변환하는 것을 의미하며, 형변환 이라고도 한다.

  • 자바의 상속 관계에 있는 부모와 자식 클래스 간에는 서로 간의 형변환이 가능하다.

업캐스팅(Up-Casting)

업캐스팅(Up-Casting)은 자식클래스(Sub-Class) 객체를 부모클래스(Super-Class) 타입으로 형변환하는 것을 의미한다.

  • 자동으로 타입이 변환되는 promotion방식으로 처리된다.
  • 업캐스팅된 서브클래스 객체는 슈퍼클래스의 메소드만 호출이 가능하다.

아래 코드에서 부모클래스는 Person, Person의 자식클래스는 Student, Student의 자식클래스는 Alba이다.

public class Main {

	public static void main(String[] args) {
		
		// UpCasting
		// 슈퍼클래스 객체 = new 서브클래스();
		Person alba = new Alba();
		alba.eat();
		alba.study();
		alba.work();
		
		// new Student()와 new Alba()는 모두
		// Person 타입으로 처리할 수 있다.
		
		Person[] people = new Person[10];
		
		people[0] = new Alba();
		people[1] = new Alba();
		people[2] = new Student();
		
		for(int i = 0; i < people.length; i++) {
			if(people[i] != null) {
			people[i].eat();
			people[i].study();
			people[i].work();
			}
		}
		
		for(Person person : people) {
			if(person != null) {
				person.eat();
				person.study();
				person.work();
			}
		}
	}

}

public class Person {

	public void eat() {
		System.out.println("먹는다.");
	}
	
	// Person 타입의 객체가 호출할 수 있도록 선언해놓은 메소드.
	public void study() {} // Up-Casting
	public void work() {} 
	
}

public class Student extends Person{
	
	@Override
	public void study() { // OverRiding
		System.out.println("공부한다.");
	}
	
}

public class Alba extends Student {

	@Override
	public void work() {
		System.out.println("일한다.");
	}
}

원래 Person클래스에서는 study와 work를 접근을 할 수 없는데, Up-Casting을 통해 접근이 가능해졌다.

다운캐스팅(Down-Casting)

업캐스팅과 반대로 업캐스팅 된 것을 다시 원상태로 되돌리는 것을 말한다.

  • 강제로 타입을 반환하는 Casting 방식으로 처리해야함
  • 업캐스팅의 문제를 해결하기 위한 또 다른 방법이다.

아래 코드에서 부모클래스는 Person, Person의 자식클래스는 Student, Student의 자식클래스는 Alba이다.

public class Main {

	public static void main(String[] args) {
		
		// 클래스타입 		: Person
		// 객체(인스턴스) 	: p
		
		Person p = new Alba(); // 업캐스팅(Up Casting)
		Person q = new Student(); // 업캐스팅(Up Casting)
		
		// instanceof 연산자
		// 특정 인스턴스가 어떤 클래스 타입인지 점검하는 연산자
		// 해당 클래스타입이면 true반환, 아니면 false반환
		
		System.out.println(p instanceof Person);
		System.out.println(p instanceof Student);
		System.out.println(p instanceof Alba);
		System.out.println();
		System.out.println(q instanceof Person);
		System.out.println(q instanceof Student);
		System.out.println(q instanceof Alba);
		
		
		// p가 Student타입의 인스턴스이면 study()
		if(q instanceof Student) {
			((Student) q).study(); // 공부한다. 다운캐스팅 자동완성
		}
		// 캐스팅은 실행하면 무조건바뀌므로 주로 if문 안에 넣어준다.
		// 조심해서사용해야한다. 실행하기전엔 오류가 있는지 모르는경우가 많음.
		
		if(q instanceof Alba) {
			((Alba) q).work();  // Alba타입을 인스턴스로 가지고있지않아서 호출이 되지않는다.
		}
		
		if(p instanceof Alba) {
			((Alba) p).work(); // 일한다.
		}
		
		
	}

}

public class Person {
	
	public void eat() {
		System.out.println("먹는다.");
	}
	
}

public class Student extends Person{

	public void study() {
		System.out.println("공부한다.");
	}
	
}

public class Alba extends Student {

	public void work() {
		System.out.println("일한다.");
	}
	
}

업캐스팅 예제

25인승 버스를 탄 손님을 좌석마다 표시해주기. (예 : 1, 비어있음 / 2, "kim" / 3, 비어있음

Main

public class Main {

	public static void main(String[] args) {
		
		Bus bus = new Bus(25);
		Seat[] seats = new Seat[25];

		bus.ride(1, new Person("kim"));
		bus.ride(1, new Person("lee")); // 중복된 경우 앉지 못함
		bus.ride(4, new Student("choi"));
		bus.ride(6, new Person("min"));
		bus.info();

// console
1, kim
2, 비어 있음
3, 비어 있음
4, choi
5, 비어 있음
6, min
7, 비어 있음
8, 비어 있음
9, 비어 있음
10, 비어 있음
11, 비어 있음
12, 비어 있음
13, 비어 있음
14, 비어 있음
15, 비어 있음
16, 비어 있음
17, 비어 있음
18, 비어 있음
19, 비어 있음
20, 비어 있음
21, 비어 있음
22, 비어 있음
23, 비어 있음
24, 비어 있음
25, 비어 있음
	}

}

Bus

public class Bus {
	
	private Seat[] seats; // 배열 선언 (배열 선언한다고 못씀)
	private int limit; // 버스 정원
	
	// Bus 생성자에서 배열 선언을 진행한다.
	
	public Bus(int cnt) {
		seats = new Seat[cnt]; // 배열 생성, new Bus(25)인 경우 Seat가 25개 생김.
		limit = cnt;
		for(int i = 0; i < cnt; i++) {
			seats[i] = new Seat();
		}
	}
	
	// ride 메소드
	public void ride(int seatNo, Person person) { // new Person, new Student, new Alba를 모두 저장할 수 있다.
		// 존재하지 않는 시트번호
		if(seatNo < 0 || seatNo > limit) {
			return; // ride() 메소드 종료
		}
		
		// 시트번호에 Person 저장하기
		Seat seat = seats[seatNo - 1];
		Person p = seat.getPerson();
		if(p == null) { // 이미 앉은 시트에 중복값 막는 코드
			seat.setPerson(person);
		}
		// 1~25를 저장하고 있기 때문에 0~24로 바꾸기위해 -1 해준다.
		
	}
	
	public void info() {
		for(int i = 0; i < limit; i++) { // limit은 seats 배열의 length와 같음.
			Seat seat = seats[i];
			Person person = seat.getPerson();
			if(person != null) { // if(seat.getPerson() !=
				// Seat seat = seats[i]; 없애고 if(seats[i] != null 하면 됨
				System.out.println((i + 1) + ", " + person.getName());// Person person = seat.getPerson(); 없애고
				//System.out,println((i + 1) + "," + person.getName()));
				// system.out.println(i + 1) + ", " + seats[i].getPerson().getName()
			} else {
				System.out.println((i + 1) + ", 비어 있음");
			}
			
		}
	}
	
	
}

Seat

public class Seat {
	
	// Person, Student, Alba를 모두 저장할 수 있는 타입은 Person
	private Person person; // upcasting

	// Seat 생성자를 생략하면
	// public Seat() { } -> 디폴트 생성자가 사용됨
	// new Seat()를 이용한 시트 생성이 가능함
	public Person getPerson() {
		return person;
	}

	public void setPerson(Person person) {
		this.person = person;
	}
	
	// getter setter를 해놔야 사람을 앉힐 수 있음.
	
}

Person

public class Person {
	
	// 상속의 기본 부모클래스는 공통여부를 만들어준다.
	private String name;

	public Person(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}

Student


public class Student extends Person{

	public Student(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}
	
}

Alba

public class Alba extends Student{

	public Alba(String name) {
		super(name);
	}

}

마트에서 장을 보는 프로그램을 완성하라.
클래스 : Product, Snack, Meat, Milk, Customer

- Snack, Meat, Milk는 모두 Product이다.
- 모든 Product는 이름(name)과 가격(price)만 가진다.
- 고객(Customer)은 모든 Product를 10개 담을 수 있는 cart를 가진다.
-고객은 돈(money)과 보너스 포인트(bonusPoint)를 가진다.
Customer customer = new Customer(); // 아직 돈이 없는 고객
customer.setMoney(10000); // 10000원이 생긴 고객
customer.buy(new Snack("홈런볼", 1500)); // 1500원짜리 홈런볼을 산다. (카트에 담는다.)
customer.buy(new Meat("한우", 5000)); // 5000원짜리 한우를 산다.
customer.buy(new Milk("서울우유", 2500)) // 2500원짜리 서울우유를 산다.
customer.receipt(); // 영수증을 본다.

/*
홈런볼 1500원
한우 5000원
서울우유 2500원

-----------------
구매총액 9000원
보너스 	 900원
남은돈	1000원

*/
Main

public class CartMain {

	public static void main(String[] args) {
		
		Customer customer = new Customer(); // default 생성자. 생성자 생략 가능.
		customer.setMoney(10000);
		customer.buy(new Meat("한우" , 5000));
		customer.buy(new Meat("불고기", 5000));
		customer.buy(new Snack("홈런볼" , 1500));
		customer.buy(new Milk("서울우유" , 2500));
		 // 구매불가
		
		customer.receipt(); // 영수증을 본다.
 
 //console
불고기를 사려면 돈이4000원 부족합니다.
한우  5000원
홈런볼  1500원
서울우유  2500-------------------------
구매총액 : 9000원
보너스   : 900.0원
거스름돈 : 1000}

}

Customer

public class Customer {
	// field
	private int money;
	private double bonusPoint;
	int total;
	private Product[] cart = new Product[10]; // cart안에 담을 물건 배열 선언
	private int idx; // 카트에 몇번째 담긴 물건인지 알아야할 때 쓰임. (카트에 담긴 물건 수)
	
	
	
	// constructor
	public Customer() { // 생성자 생략. new Customer()가능
		
	}
	
	
	// method
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
	
	public double getBonusPoint() {
		return bonusPoint;
	}


	public void setBonusPoint(int bonusPoint) {
		this.bonusPoint = bonusPoint;
	}
	
	
	// 구매 메소드
	public void buy(Product product) { // UpCasting사용
		int price = product.getPrice(); // Refactoring (성능상 이점)
		// 가진 돈보다 비싼 물건은 사지 못한다.
		if(money < price) { // product.getPrice = 상품의 가격
			System.out.println(product.getName() + "를 사려면 돈이" + (price - money) + "원 부족합니다.");
			return;
		}
		// 카트가 가득 차면 물건을 못 산다.
		if(idx == cart.length) { // idx가 cart의 길이가 되면(max) 물건 못삼
			System.out.println("카트가 가득 찼습니다.");
			return;
		}
		// 구매
		cart[idx++] = product; // 상품을 카트에 넣고 인덱스의 숫자가 오른다(후위연산자)
		total += price;
		money -= price;
		bonusPoint += price * 0.1; // 포인트는 구매가격의 10퍼센트 늘어난다.
	}
	
	public void receipt() {
		// 물건을 안 샀다.
		if(idx == 0) {
			System.out.println("구매한 물건이 없습니다.");
			return;
		}
		// 구매 총액 구하기
		
		for(int i = 0; i < idx; i++) { // 인덱스 까지 for문 돌리기 NullPointExeption 회피하는 방법
			Product product = cart[i];
			System.out.println(product.getName() + "  " + product.getPrice() + "원");
			
		}
	
		System.out.println("-------------------------");
		System.out.println("구매총액 : " + total + "원");
		System.out.println("보너스   : " + bonusPoint + "원");
		System.out.println("거스름돈 : " + money + "원"); 
		// 구매할때마다 위에서 money값이 줄어들고 있기 때문에 money라고 해도된다.
		
		
	}

	
	


}

Product

public class Product {
	private String name;
	private int price;

	public Product(String name, int price) {
		super();
		this.name = name;
		this.price = price;

	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}
}

Snack

public class Snack extends Product {

	public Snack(String name, int price) {
		super(name, price);
		// TODO Auto-generated constructor stub
	}

}

Meat

public class Meat extends Product {

	public Meat(String name, int price) {
		super(name, price);
	}

}

Milk

public class Milk extends Product {

	public Milk(String name, int price) {
		super(name, price);
	}

}

다운캐스팅 예제

주차장에 10대의 차량이 주차되어있다.
각 33퍼센트확률로 자동차, 전기차, 하이브리드차가 존재하는데,
자동차가 나올경우 운전한다, 전기차가 나올경우 충전한다 하이브리드차가 나올경우 기름넣는다를 출력하라.

메인클래스

public class Main {

	public static void main(String[] args) {

// 첫번째방법
//		String[] cars = new String[10];
//		String[] type = {"Car", "Ev", "Hybrid"};
//		
//		Car c = new Hybrid();
//		
//		//랜덤
//		for(int i = 0; i < cars.length; i++) {
//			int j = (int)(Math.random() * 3) + 0;
//			cars[i] = type[j];
////			System.out.println(cars[i]);
//		
//			if(cars[i] == type[0]) {
//				if(c instanceof Car) {
//					c.drive();
//				}
//			} else if (cars[i] == type[1]) {
//				if(c instanceof Ev) {
//					((Ev) c).charge();
//				}
//			} else {
//				if(c instanceof Hybrid) {
//					((Hybrid) c).addOil();
//				}
//			}
//		
//		}
		
		
// 두번째방법		
		Car[] cars = new Car[10];
		for(int i=0; i < cars.length; i++) {
			if(Math.random() < 0.33) {
				cars[i] = new Car();
			} else if (Math.random() < 0.66) {
				cars[i] = new Ev();
			} else {
				cars[i] = new Hybrid();
			}

		}			
		
		/*
		   Car 이면 drive()호출 
		   Ev이면 charge()호출 
		   Hybrid이면 addOil()호출
		 */
		
		// 부모와 자식간의 관계라면 자식부터 호출해주어야
		// 상속관계에 얽메이지 않는다.
		// 만약 부모부터 호출한다면 모두 부모로 볼것이다.
		
		for(int i = 0; i < cars.length; i++) {
			if(cars[i] instanceof Hybrid) {
				((Hybrid) cars[i]).addOil();
			} else if(cars[i] instanceof Ev) {
				((Ev) cars[i]).charge();
			} else if(cars[i] instanceof Car) {
				cars[i].drive();
			} 
		}

	}

}

둘다 같은 출력이다. (랜덤확률)

자동차클래스

public class Car {

	public void drive() {
		System.out.println("운전한다.");
	}
	
}

전기차클래스

public class Ev extends Car{

	public void charge() {
		System.out.println("충전한다.");
	}
	
}

하이브리드차클래스

public class Hybrid extends Ev{

	public void addOil() {
		System.out.println("기름 넣는다.");
	}
	
}
profile
자몽 허니 블랙티와 아메리카노 사이 그 어딘가

0개의 댓글