JAVA_상속_Method Overriding과 Up / Down Casting

JW__1.7·2022년 8월 1일
2

JAVA 공부일지

목록 보기
15/30

메소드 오버라이딩 (Method Overriding)

  • 메소드 덮어쓰기
  • 슈퍼클래스의 메소드를 서브클래스에서 재정의하는 것
  • 슈퍼클래스의 메소드를 서브클래스가 사용하지 못하는 경우 메소드 오버라이딩이 필요함
  • 반드시 슈퍼클래스의 메소드와 동일한 원형(반환타입, 메소드명,
    오버라이드 된 메소드 앞에는 @Override 애너테이션을 작성해서 오버라이드 된 메소드임을 알림
  • 부모가 가지고 있는 메소드가 나하고 안 맞아서 다시 만드는 것이 메소드 오버라이딩이다.
  • 만들 때 규칙 딱 하나만 잘 지키면 되는데 부모랑 똑같이 만들면 된다.
    오버라이드 했을 때, @Override 를 적어주면 좋다. 다른 사람들도 인식할 수 있기 때문이다.

에스프레소 맛이 쓰다

package ex01_override;
public class Coffee {

	// 커피 원두를 의미함
	public void taste() {
	}
}
package ex01_override;
public class Espresso extends Coffee {
	
	// 메소드 오버라이딩
	// 유일한 규칙은 똑같은 모양으로 만들면 된다.
	// 권장 규칙은 @Override 추가
	
	@Override
	public void taste() {
		System.out.println("쓰다");
	}
}
package ex01_override;
public class EspressoMain {
	public static void main(String[] args) {
		
		Espresso espresso = new Espresso();
		espresso.taste();	// 쓰다
	}
}

에스프레소에 extraWater를 추가하면 Americano
에스프레소에 extraMilk를 추가하면 CafeLatte

package ex01_override;
public class Americano extends Espresso {
	
    private int extraWater;
    
	@Override
	public void taste() {
		System.out.println("덜쓰다");
	}
}
package ex01_override;
public class CafeLatte extends Espresso {

	private int extraMilk;
    
	@Override
	public void taste() {
		System.out.println("부드럽다");
	}
}
package ex01_override;
public class EspressoMain {
	public static void main(String[] args) {
		
		Espresso espresso = new Espresso();
		espresso.taste();	// 쓰다
		
		Americano americano = new Americano();
		americano.taste();	// 덜쓰다
		
		CafeLatte cafeLatte = new CafeLatte();
		cafeLatte.taste();	// 부드럽다
	}
}

도형의 종류

package ex02_override;
public class Shape {

	private String type;	// 도형의 종류

	public Shape(String type) {
		super();	// 신경쓰지 않는다.
		this.type = type;
	}
	
	public double getArea() {
		return 0;
	}
	public void info() {
		System.out.println("도형의 종류 : " + type);
	}
}
package ex02_override;
public class Circle extends Shape {

	private double radius;

	public Circle(String type, double radius) {
		super(type);
		this.radius = radius;
	}

	@Override
	public double getArea() {
		return Math.PI * Math.pow(radius, 2);
	}
	@Override
	public void info() {
		super.info();
		System.out.println("반지름 : " + radius);
		System.out.println("넓이 : " + getArea());
	}	
}
package ex02_override;
public class Main {
	public static void main(String[] args) {

		Circle circle = new Circle("도넛", 7.5);
		circle.info();
						// 도형의 종류 : 도넛
	}					// 반지름 : 7.5
}						// 넓이 : 176.71458676442586

너비, 높이를 가지는 사각형(Rectangle)
너비, 높이를 서로 같은 정사각형(Square)

package ex02_override;
public class Rectangle extends Shape {
	
	private double width;
	private double height;
	
	public Rectangle(String type, double width, double height) {
		super(type);
		this.width = width;
		this.height = height;
	}
	
	@Override
	public double getArea() {
		return width * height;
	}
	@Override
	public void info() {
		super.info();
		System.out.println("너비 : " + width + " / " + "높이 : " + height);
		System.out.println("넓이 : " + getArea());
	}	
}
package ex01_override;
public class Square extends Rectangle{

	public Square(String type, double width) {
		super(type, width, width);
	}
}

Override 대상이 아니라서 만들지 않고, Rectangle 것을 그대로 사용한다.

package ex02_override;
public class Main {
	public static void main(String[] args) {

		Circle circle = new Circle("도넛", 7.5);
		circle.info();

		Rectangle rectangle = new Rectangle("사각형", 5, 7);
		rectangle.info();
		
		Square square = new Square("정사각형", 5);
		square.info();
	}
}

반환값

도형의 종류 : 도넛
반지름 : 7.5
넓이 : 176.71458676442586
도형의 종류 : 사각형
너비 : 5.0 / 높이 : 7.0
넓이 : 35.0
도형의 종류 : 정사각형
너비 : 5.0 / 높이 : 5.0
넓이 : 25.0

Quiz 1

Coffee - Espresso - Americano 상속
Espresso 케냐 원두, 물 50ml
Americano 케냐 원두, 물 300ml, 아이스 아메리카노

package quiz01_coffee;
public class Coffee {
	
	private String origin;
	
	public Coffee(String origin) {
		super();
		this.origin = origin;
	}
	
	public void info() {
		System.out.println(origin + "원두");
	}
}
package quiz01_coffee;
public class Espresso extends Coffee {

	private int water;
	
	public Espresso(String origin, int water) {
		super(origin);
		this.water = water;
	}

	@Override
	public void info() {
		super.info();
		System.out.println("물 " + water + "ml");
	}
}
package quiz01_coffee;
public class Americano extends Espresso {

	private String type;

	public Americano(String origin, int water, String type) {
		super(origin, water);
		this.type = type;
	}

	@Override
	public void info() {
		super.info();
		System.out.println(type + " 아메리카노");
	}
}
package quiz01_coffee;
public class Main {
	public static void main(String[] args) {

		Espresso espresso = new Espresso("케냐", 50);
		espresso.info();	// 케냐 원두, 물 50ml
		
		Americano americano = new Americano("케냐", 300, "아이스");
		americano.info();	// 케냐 원두, 물 300ml, 아이스 아메리카노	
	}
}

반환값

케냐원두
물 50ml
케냐원두
물 300ml
아이스 아메리카노

Quiz 2

Coffee - Espresso 상속, Americano는 Espresso 상속이 아닌 포함하는 형태로
Espresso 케냐 원두, 물 50ml
Americano 케냐 원두, 물 50ml 2샷, 아이스 아메리카노

package quiz02_coffee;
public class Coffee {
	
	private String origin;
	
	public Coffee(String origin) {
		super();
		this.origin = origin;
	}
	
	public void info() {
		System.out.println(origin + "원두");
	}
}
package quiz02_coffee;
public class Espresso extends Coffee {

	private int water;
	
	public Espresso(String origin, int water) {
		super(origin);
		this.water = water;
	}

	@Override
	public void info() {
		super.info();
		System.out.println("물 " + water + "ml");
	}
}
package quiz02_coffee;
public class Americano {

	private Espresso espresso;
	private int shot;
	private String type;
	
	public Americano(Espresso espresso, int shot, String type) {
		super();
		this.espresso = espresso;
		this.shot = shot;
		this.type = type;
	}
	
	public void info() {
		espresso.info();
		System.out.println(shot + "샷");
		System.out.println(type + " 아메리카노");
	}
}
package quiz02_coffee;
public class Main {
	public static void main(String[] args) {

		Espresso espresso = new Espresso("케냐", 50);
		espresso.info();	// 케냐 원두, 물 50ml
		
		Americano americano = new Americano(espresso, 2, "아이스");
		americano.info();	// 케냐 원두, 물 50ml 2샷, 아이스 아메리카노	
	}
}

반환값

케냐원두
물 50ml
케냐원두
물 50ml
2샷
아이스 아메리카노

업캐스팅 (Up-Casting) ★

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

업캐스팅과 메소드 오버라이드 ★

  • 업캐스팅된 서브클래스 객체는 슈퍼클래스의 메소드만 호출할 수 있다.
    따라서 슈퍼클래스에 서브클래스의 메소드를 정의해 주고 서브클래스가 오버라이드 하는 방식으로 서브클래스의 메소드를 호출한다.
  • 슈퍼클래스 Car에 있는 요금을낸다( )벨을누른다( )는 호출용이고, 실제로는 서브클래스 Bus에 있는 것으로 실행한다.
package ex01_upcasting;
public class Person {

	public void eat() {
		System.out.println("먹는다.");
	}
	
	// Person 타입의 객체가 호출할 수 있도록 추가해 둔 메소드
	public void study() {}
	public void work() {}	
}
package ex01_upcasting;
public class Student extends Person {
	
	@Override
	public void study() {
		System.out.println("공부한다.");
	}
}
package ex01_upcasting;
public class Alba extends Student {
	
	@Override
	public void work() {
		System.out.println("일한다.");
	}
}

UpCasting
슈퍼클래스 객체 = new 서브클래스( );

package ex01_upcasting;
public class Main {
	public static void main(String[] args) {
		
		Person alba = new Alba();
		alba.eat();		// 먹는다
		alba.study();	// 공부한다
		alba.work();	// 일한다

new Student( )와 new Alba( )는 모두 Person 타입으로 처리할 수 있다.

한 교실에 Student와 Alba가 섞여 있다.
어떻게 처리할 것인가?
Person 타입의 배열을 이용해서 모두 처리할 수 있다.

		Person[] people = new Person[10];
		
		people[0] = new Alba();		// 먹,공,알
		people[1] = new Alba(); 	// 먹,공,알
		people[2] = new Student();	// 먹,공
		// 이렇게 사용하면 안된다.
        // WHY? 배열 값을 3개만 작성했기 때문에 나머지는 null값이다.
		
		// 일반 for문
		for(int i = 0; i < people.length; i++) {
			if(people[i] != null) {
				people[i].eat();
				people[i].study();
				people[i].work();
			}
		}
		
		// 향상 for문
		for(Person person : people) {
			if(person != null) {
				person.eat();
				person.study();
				person.work();
			}
		}	
	}
}

Quiz 3

좌석 앉기

package quiz03_bus;
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;
	}
}
package quiz03_bus;
public class Student extends Person {

	public Student(String name) {
		super(name);
	}	
}
package quiz03_bus;
public class Alba extends Student {

	public Alba(String name) {
		super(name);
	}
}
package quiz03_bus;
public class Seat {
	
	// Person, Student, Alba를 모두 저장할 수 있는 타입은 Person
	private Person person;

	// Seat 생성자를 생략하면
	// public Seat( ) { } → 디폴트 생성자가 사용됨
	// new Seat( )를 이용한 시트 생성이 가능함
	
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
}
package quiz03_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) {
		// 존재하지 않는 시트번호
		if(seatNo <= 0 || seatNo > limit) {
			return;	  // ride( ) 매소드 종료
		}
		// 시트에 사람이 없으면, 시트번호에 Person 저장하기
		Seat seat = seats[seatNo - 1];
		Person p = seat.getPerson();
		if(p == null) {
			seat.setPerson(person);
		}
	}
	
	// info( ) 메소드
	public void info() {
		for(int i = 0; i < limit; i++) {	// limit은 seats 배열의 length와 같음
			Seat seat = seats[i];
			Person person = seat.getPerson();	// Person person = seats[i].getPerson();
			if(person != null) {  // if(seat.getPerson() != null), if(seats[i].getPerson() != null)
				System.out.println((i + 1) + "," + person.getName());
				// System.out.println((i + 1) + "," + seat.getPerson().getName());
				// System.out.println((i + 1) + "," + seats[i].getPerson().getName());
			} else {
				System.out.println((i + 1) + ", 비어 있음");
			}
		}
	}
}

1번째 좌석에 김씨 사람이 타고, 5번째 좌석에 최씨 학생이 타고, 10번째 좌석에 민씨 알바가 탔다.

package quiz03_bus;
public class Main {
	public static void main(String[] args) {

		Bus bus = new Bus(25);
		bus.ride(1, new Person("kim"));
		bus.ride(1, new Person("lee"));
		bus.ride(5, new Student("choi"));
		bus.ride(10, new Alba("min"));
		bus.info();
	}
}

반환값

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

Quiz 4

장보기

모든 Product은 이름(name)과 가격(price)만 가진다.

package quiz04_cart;
public class Product {

	private String name;
	private int price;
	
	public Product(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}
	// 영수증 만들 때 이름(name)과 가격(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), 고기(Meat), 우유(Milk는)는 모두 Product이다.

package quiz04_cart;
public class Snack extends Product {

	public Snack(String name, int price) {
		super(name, price);
	}
}
package quiz04_cart;
public class Meat extends Product {

	public Meat(String name, int price) {
		super(name, price);
	}
}
package quiz04_cart;
public class Milk extends Product {

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

고객(Customer)은 모든 Product을 10개 담을 수 있는 cart를 가진다.
고객은 돈(money)과 보너스포인트(bonusPoint, 구매액의 10%)를 가진다.

package quiz04_cart;
public class Customer {
	
	// 필드
	private int money;
	private int bonusPoint;
	private int total;
	private Product[] cart = new Product[10];
	private int idx;  // cart에 담긴 Product의 개수. cart 배열의 인덱스.

	// 생성자 생략
	// new Customer() 가능
		
	// 메소드
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {	// 실제로는 setMoney만 사용
		this.money = money;
	}
	public int getBonusPoint() {
		return bonusPoint;
	}
	public void setBonusPoint(int bonusPoint) {
		this.bonusPoint = bonusPoint;
	}
		
	// buy() 메소드
	public void buy(Product product) {		// UpCasting 사용
		int price = product.getPrice();
		// 가진 돈보다 비싼 물건은 못 산다.
		if(money < price) {
			System.out.println(product.getName() + " 사려면 돈이 " + (price - money) + "원 부족합니다.");
			return;
		}
		// 카트가 가득 차면 물건을 못 산다.
		if(idx == cart.length) {
			System.out.println("카트가 가득 찼습니다.");
			return;
		}
		// 구매
		cart[idx++] = product;
		money -= price;
		total += price;
		bonusPoint += price * 0.1;
	}
		
	// receipt() 메소드
	public void receipt() {
		System.out.println();
		System.out.println("======== 영수증 ========");
		// 물건을 안 샀다.
		if(idx == 0) {
			System.out.println("구매한 물건이 없습니다.");
			return;
		}
		// 구매 총액 구하기 및 출력
		for(int i = 0; i < idx; i++) {
			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 + "원");
	}
}
package quiz04_cart;
public class Main {
	public static void main(String[] args) {

		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.buy(new Meat("불고기", 5000));		// 구매 불가
		customer.receipt();							// 영수증을 본다.
	}
}

반환값

돈이 4000원 부족합니다.
홈런볼 : 1500원
한우 : 5000원
서울우유 : 2500-----------------------
구매총액 : 9000원
보너스 : 900원
거스름돈 : 1000

다운캐스팅 (Down-Casting)

  • 업캐스팅 된 서브클래스 객체를 다시 서브클래스 타입으로 변환하는 것
  • 강제로 타입을 변환하는 casting 방식으로 처리
  • 업캐스팅의 문제를 해결하기 위한 또 다른 방법
  • 캐스팅은 실행하기 전에 오류인지 알 수 없어서 신중하게 사용해야 한다.
package ex01_downcasting;
public class Person {
	
	public void eat() {
		System.out.println("먹는다");
	}
}
package ex01_downcasting;
public class Student extends Person {
	
	public void study() {
		System.out.println("공부한다");
	}
}
package ex01_downcasting;
public class Alba extends Student {

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

instanceof 연산자

  • 특정 인스턴스가 어떤 클래스 타입인지 점검하는 연산자
  • 해당 클래스 타입이면 true 반환, 아니면 false 반환
package ex01_downcasting;
public class Main {
	public static void main(String[] args) {

		// 클래스 타입 : Person
		// 객체(인스턴스) : p
		Person p = new Alba();		// 업캐스팅
		
		// instanceof 연산자
		System.out.println(p instanceof Person);
		System.out.println(p instanceof Student);
		System.out.println(p instanceof Alba);
		
		// p가 Student타입의 인스턴스이면 study() 메소드를 호출할 수 있다.
		if(p instanceof Student) {
			((Student) p).study();		// 다운캐스팅 p.study() 자동 완성으로 작성
		}	
		
		// p가 Alba타입의 인스턴스이면 work() 메소드를 호출할 수 있다.
		if(p instanceof Alba) {	
			((Alba) p).work();			// 다운캐스팅 p.work() 자동 완성으로 작성
		}
	}
}

반환값

true
true
true
공부한다
일한다

임의의 자동차 10대 배열에 저장하기 (33% 확률로 램덤 생성)

package ex02_downcasting;
public class Car {
	
	public void drive() {
		System.out.println("운전하다");
	}
}
package ex02_downcasting;
public class Ev extends Car {

	public void charge() {
		System.out.println("충전하다");
	}
}
package ex02_downcasting;
public class Hybrid extends Ev {
	
	public void addOil() {
		System.out.println("기름을넣는다");
	}
}
package ex02_downcasting;
public class Main {
	public static void main(String[] args) {

		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() 호출

하위클래스인 Hybrid부터 먼저 체크해야 실행된다.

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

반환값

충전하다
기름을넣는다
충전하다
충전하다
기름을넣는다
운전하다
운전하다
기름을넣는다
운전하다
기름을넣는다

0개의 댓글