자바의 정석 | Ch 07 - 객체지향 프로그래밍2-2
by 무작정 개발다형성
다형성이란?
객체지향 개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미하며, 자바에서는
한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로
구현했다.
장점
- 부모 타입 참조 변수로 자식 타입 객체를 다루는 것
- 하나의 배열에 여러 종류의 객체 저장
참조 변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.
참조 변수 타입과 인스턴스 타입은 보통 일치하지만 일치하지 않을 수도 있다.
참조 변수의 형 변환
부모. 자식(상속관계)의 참조 변수는 서로 형 변환이 가능하다.
자식 타입 -> 부모 타입(up-casting) : 형 변환 생략 가능
부모 타입 -> 자식 타입(down-casting) : 형 변환 생략 불가
javapackage Chapter07; class Car { String color; int door; void drive() { System.out.println("drive, 부릉부릉~"); } void stop() { System.out.println("stop, 멈춰!정지"); } } class FireEngine extends Car { void water() { System.out.println("물뿌리기!"); } } //부모 클래스 : Car / 자식 클래스 : FireEngine public class CastingTest1 { public static void main(String[] args) { // Car 클래스, FireEngine 클래스는 상속관계 Car car = null; // Car타입 참조변수 car를 선언하고 null로 초기화 FireEngine fe = new FireEngine(); //인스턴스(객체)생성 / 포함관계 FireEngine fe2 = null; fe.water(); car = fe; // 자식 -> 부모 : up-casting / 형변환 생략가능 //car.water(); // 에러! fe = (FireEngine)car; // 부모->자식 : down-casting fe2.water(); } }
instance of 연산자
참조 변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof연산자를 사용
- 참조 변수의 형 변환 가능 여부 확인용. 가능하면 true 반환
- 형 변환 전에 반드시 instanceof로 확인해야 함
- 주로 조건문에 사용 -> if(참조 변수 instanceof 타입(클래스명))
★ instanceof연산의 결과가 true가 나오면 검사한 타입으로 형 변환이 가능하다는 것을 뜻함
매개변수의 다형성
- 참조형 매개변수는 메서드 호출 시 자신과 같은 타입 또는 자식 타입의 인스턴스를 넘겨줄 수 있다.
여러 종류의 객체를 배열로 다루기
- 부모 타입의 배열에 자식 타입 객체를 담을 수 있다.
- 다루고 싶은 객체들의 상속관계를 따져서 가장 가까운 공통조상 클래스 타입의 참조 변수 배열을 생성해서 객체들을 저장
Product클래스가 Tv, Computer, Audio클래스의 부모일 때 하단과 같이 가능
javaProduct p1 = new Tv(); Product p2 = new Computer(); Product p3 = new Audio();
위를 Product타입의 참조 변수 배열로 처리하면 하단과 같다.
javaProduct p [] = new Product[3]; p[0] = new Tv(); p[1] = new Computer(); p[2] = new Audio();
javapackage Chapter07; import java.util.*; // Vector클래스를 사용하기 위해서 추가 class Product { int price; // 제품의 가격 int bonusPoint; // 제품구매 시 제공하는 보너스점수 Product(int price) { this.price = price; bonusPoint =(int)(price/10.0); } Product() { price = 0; bonusPoint = 0; } } class Tv2 extends Product { Tv2() { super(100); } public String toString() { return "Tv"; } } class Computer extends Product { Computer() { super(200); } public String toString() { return "Computer"; } } class Audio extends Product { Audio() { super(50); } public String toString() { return "Audio"; } } class Buyer { // 고객, 물건을 사는 사람 int money = 1000; // 소유금액 int bonusPoint = 0; // 보너스점수 Vector item = new Vector(); // 구입한 제품을 저장하는데 사용될 Vector객체 void buy(Product p) { if(money < p.price) { System.out.println("잔액이 부족하여 물건을 살수 없습니다."); return; } money -= p.price; // 가진 돈에서 구입한 제품의 가격을 뺀다. bonusPoint += p.bonusPoint; // 제품의 보너스 점수를 추가한다. item.add(p); // 구입한 제품을 Vector에 저장한다. System.out.println(p + "을/를 구입하셨습니다."); } void refund(Product p) { // 구입한 제품을 환불한다. if(item.remove(p)) { // 제품을 Vector에서 제거한다. money += p.price; bonusPoint -= p.bonusPoint; System.out.println(p + "을/를 반품하셨습니다."); } else { // 제거에 실패한 경우 System.out.println("구입하신 제품 중 해당 제품이 없습니다."); } } void summary() { // 구매한 물품에 대한 정보를 요약해서 보여준다. int sum = 0; // 구입한 물품의 가격합계 String itemList =""; // 구입한 물품목록 if(item.isEmpty()) { // Vector가 비어있는지 확인한다. System.out.println("구입하신 제품이 없습니다."); return; } // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다. for(int i=0; i<item.size();i++) { Product p = (Product)item.get(i); sum += p.price; itemList += (i==0) ? "" + p : ", " + p; } System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다."); System.out.println("구입하신 제품은 " + itemList + "입니다."); } } public class PolyArgumentTest3 { public static void main(String args[]) { Buyer b = new Buyer(); Tv2 tv = new Tv2(); Computer com = new Computer(); Audio audio = new Audio(); b.buy(tv); b.buy(com); b.buy(audio); b.summary(); System.out.println(); b.refund(com); b.summary(); } }
추상 클래스 - (abstract class)
클래스를 설계도에 비유하자면, 추상 클래스는 미완성 설계도에 비유할 수 있다.
- 미완성 메서드를 갖고 있는 클래스
- 다른 클래스 작성에 도움을 주기 위한 것 / 인스턴스 생성 불가
- 상속을 통해 추상 메서드를 완성해야 인스턴스 생성 가능
추상 클래스 상속 -> 추상 메서드의 구현부 완성 -> 완성도니 설계도(객체 생성)
추상 메서드 - (abstract method)
메서드는 선언부와 구현부로 구성되어 있는데 선언 부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상 메서드
- 공통적으로 꼭 필요하지만 자식마다 다르게 구현될 것으로 예상되는 경우 사용
- 추상 메서드를 1개라도 구현하지 않으면 여전히 미완성
- 추상 클래스로부터 상속받는 자식 클래스는 오버 라이딩을 통해 부모인 추상 클래스의 추상 메서드를 모두 구현해주어야 한다.
- 여러 클래스에 공통적으로 쓸 수 있는 추상 클래스를 바로 작성하거나 기존 클래스의 공통된 부분을 뽑아서 추상 클래스를 작성
추상 클래스의 작성
장점
- 설계도를 쉽게 작성 가능
- 코드 중복제거
- 코드 관리가 용이함
- 구체화된 코드보다 우연(변경에 유리함)
추상화 : 클래스 간의 공통점을 찾아내서 공통의 부모를 만드는 작업
인터페이스 - (interface)
인터페이스란?
인터페이스는 일종의 추상 클래스이다. 추상 클래스처럼 추상 메서드를 갖지만 추상 클래스보다 추상화 정도가 높아 추상 클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버 변수를 구성원으로 가질 수 없다.
오직 추상 메서드와 상수만을 멤버로 가질 수 있다.
- 추상 메서드의 집합
- 구현된 것이 전혀 없는 설계도(모든 멤버가 public)
추상 메서드 : 추상 메서드를 가진 일반 클래스
인터페이스 : 추상 메서드 빼고 구현된 것이 아무것도 없다.
인터페이스 선언
javainterface 인터페이스 이름 { public static final 타입 상수이름 = 값; // 갑=상수 public abstract 메서드이름(매개변수목록); }
인터페이스의 멤버들의 제약사항
- 모든 멤버 변수는 public static final이어야 하며, 생략 가능함
- 모든 메서드는 public abstract 이어야 하며, 생략 가능함 (단, static메서드. 디폴트 메서드는 예외 JDK1.8부터)
인터페이스의 상속
- 인터페이스는 인터페이스만 상속 가능
- 클래스와 달리 다중 상속 가능
- 인터페이스는 클래스와 달리 Object클래스와 같은 최고 조상이 없다.
인터페이스의 구현
인터페이스는 추상 클래스처럼 그 자체로는 인스턴스(객체)를 생성할 수 없으며, 자신에 정의된 추상 메서드의 몸통을 만들어주는 클래스를 작성해야 함.
- 인터페이스에 정의된 추상 메서드를 완성하는 것
클래스를 상속받을 땐 'extend'를 사용하는데 인터페이스를 상속받을 때는 'implements'를 사용
javaclass 클래스이름 implements 인터페이스이름 { // 인터페이스에 정의된 추상 메서드를 모두 구현해야 함. } class Fighter implements Fightable { public void move(int x, int y) { } public void attack(Unit u) //이때 Fighter클래스는 Fightable인터페이스를 구현한다
- 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 한다.
- 인터페이스는 인스턴스 변수를 가질 수 없다.
인터페이스를 이용한 다형성
- 인터페이스도 구현 클래스의 부모이다.
- 인터페이스 타입의 매개변수는 인터페이스를 구현한 클래스의 객체만 가능하다.
- 인터페이스를 메서드의 리턴 타입으로 지정할 수 있다. (안 맞으면 형 변환) -> 이 인터페이스를 구현한 것을 반환하겠다는 뜻)
인터페이스의 장점
- 두 대상(객체) 간의 연결, 대화, 소통을 돕는 중간 역할을 함.
- 변경에 유리한 설계가능
- 선언과 구현을 분리할 수 있다.
- 개발 시간 단축 가능
- 표준화 가능
- 서로 관계없는 클래스들을 맺어줄 수 있다.
블로그의 정보
무작정 개발
무작정 개발