자바의 정석 | Ch 07 - 객체지향 프로그래밍2-2
by 무작정 개발다형성
다형성이란?
객체지향 개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미하며, 자바에서는
한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로
구현했다.
장점
- 부모 타입 참조 변수로 자식 타입 객체를 다루는 것
- 하나의 배열에 여러 종류의 객체 저장
참조 변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.
참조 변수 타입과 인스턴스 타입은 보통 일치하지만 일치하지 않을 수도 있다.
참조 변수의 형 변환
부모. 자식(상속관계)의 참조 변수는 서로 형 변환이 가능하다.
자식 타입 -> 부모 타입(up-casting) : 형 변환 생략 가능
부모 타입 -> 자식 타입(down-casting) : 형 변환 생략 불가
package 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클래스의 부모일 때 하단과 같이 가능
Product p1 = new Tv();
Product p2 = new Computer();
Product p3 = new Audio();
위를 Product타입의 참조 변수 배열로 처리하면 하단과 같다.
Product p [] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
package 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)
추상 메서드 : 추상 메서드를 가진 일반 클래스
인터페이스 : 추상 메서드 빼고 구현된 것이 아무것도 없다.
인터페이스 선언
interface 인터페이스 이름 {
public static final 타입 상수이름 = 값; // 갑=상수
public abstract 메서드이름(매개변수목록);
}
인터페이스의 멤버들의 제약사항
- 모든 멤버 변수는 public static final이어야 하며, 생략 가능함
- 모든 메서드는 public abstract 이어야 하며, 생략 가능함 (단, static메서드. 디폴트 메서드는 예외 JDK1.8부터)
인터페이스의 상속
- 인터페이스는 인터페이스만 상속 가능
- 클래스와 달리 다중 상속 가능
- 인터페이스는 클래스와 달리 Object클래스와 같은 최고 조상이 없다.
인터페이스의 구현
인터페이스는 추상 클래스처럼 그 자체로는 인스턴스(객체)를 생성할 수 없으며, 자신에 정의된 추상 메서드의 몸통을 만들어주는 클래스를 작성해야 함.
- 인터페이스에 정의된 추상 메서드를 완성하는 것
클래스를 상속받을 땐 'extend'를 사용하는데 인터페이스를 상속받을 때는 'implements'를 사용
class 클래스이름 implements 인터페이스이름 {
// 인터페이스에 정의된 추상 메서드를 모두 구현해야 함.
}
class Fighter implements Fightable {
public void move(int x, int y) { }
public void attack(Unit u)
//이때 Fighter클래스는 Fightable인터페이스를 구현한다
- 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 한다.
- 인터페이스는 인스턴스 변수를 가질 수 없다.
인터페이스를 이용한 다형성
- 인터페이스도 구현 클래스의 부모이다.
- 인터페이스 타입의 매개변수는 인터페이스를 구현한 클래스의 객체만 가능하다.
- 인터페이스를 메서드의 리턴 타입으로 지정할 수 있다. (안 맞으면 형 변환) -> 이 인터페이스를 구현한 것을 반환하겠다는 뜻)
인터페이스의 장점
- 두 대상(객체) 간의 연결, 대화, 소통을 돕는 중간 역할을 함.
- 변경에 유리한 설계가능
- 선언과 구현을 분리할 수 있다.
- 개발 시간 단축 가능
- 표준화 가능
- 서로 관계없는 클래스들을 맺어줄 수 있다.
'Language > Java' 카테고리의 다른 글
[Java] JVM와 바이트 코드 (0) | 2022.10.04 |
---|---|
[Java] JDK, JRE 이란? (0) | 2022.10.03 |
자바의 정석 | Ch 07 - 객체지향 프로그래밍2-1 (0) | 2022.02.02 |
[JAVA + 국비교육] Java 팀 프로젝트(자판기) | 소스 코드, 실행 영상, 느낀 점 (0) | 2022.01.19 |
[JAVA+국비교육] XML파일 읽어오기, 정규화 표현식 (0) | 2022.01.17 |
블로그의 정보
무작정 개발
무작정 개발