본문 바로가기

JAVA

7장_3

13. 추상 클래스와 추상 메서드

추상 클래스

추상 메서드를 갖고 있는 클래스 (미완성 설계도 = 미완성 클래스)

추상 클래스도 일반 클래스처럼 인스턴스 변수, 생성자, 메서드 모두 가질 수 있다. 단지, 플러스로 추상 메서드를 갖고 있다고 생각하면 된다.

추상 메서드를 갖고 있는 클래스에는 abstract를 붙여준다.

 

 

추상 클래스의 목적

다른 클래스 작성에 도움을 주기 위한 것으로 인스턴스 생성 불가

추상 클래스는 상속을 통해 자손이 각자의 역할에 맞게 오버라이딩하여 추상 메서드를 완성해야 인스턴스 생성 가능

 

 

추상 메서드

미완성 메서드 = 선언부만 있고 몸통{ }이 없는 메서드

추상 메서드 앞에는 abstract를 붙여준다.

 

상속을 통해 여러 개의 추상 메서드를 물려받았을 경우, 모두 구현해야줘야한다. (구현하지 않아도 에러는 없지만, 객체를 생성할 수 없음!!)

모두 구현해주지 않을거라면, 클래스에 abstract를 붙여줘야 한다.

 

 

Q) 추상 메서드로 안 하고 그냥 { } 빈 메서드 만들어서 오버라이딩하면 안 되나요?

추상 메서드는 그 기능을 꼭 구현하라는 강제성이 있고, 오버라이딩은 선택적.

가령, 개발자가 클래스에 abstract가 없어서 메서드가 모두 구현되어 있는 줄 알고

객체를 생성했는데 객체 생성이 안 되는 오류가 발생할 수 있음!

 

public class Main {
	public static void main(String[] args) {
        // 배열 생성 및 초기화
        // 조상 타입의 객체 배열로 자식 타입을 다룸
//      Unit[] group = new Unit[3];
//      group[0] = new Marine();
//      group[1] = new Tank();
//      group[2] = new Dropship();
		
        Unit[] group = { new Marine(), new Tank(), new Dropship() }; 
       
       	// group의 타입은 Unit[], group[i]의 타입은 Unit
        // 모든 객체 배열을 Unit 타입으로 다루기 위해 Unit 배열에 넣었잖아!
        // 즉, Unit 리모컨으로 group[i]를 다룬다고 생각하라.
        for(int i = 0; i < group.length; i++) {
			group[i].move(100, 200);
		}
	}
}

abstract class Unit { // 공통사항
	int x, y;
	abstract void move(int x, int y);
	void stop() {}
}

class Marine extends Unit { // 보병
	@Override
	void move(int x, int y) {
		System.out.println("Marine[x=" + x + ",y=" + y + "]");
	}
	void stimPack() { /* 스팀팩을 사용한다 */ }
}

class Tank extends Unit { // 탱크
	@Override
	void move(int x, int y) {
		System.out.println("Tank[x=" + x + ",y=" + y + "]");
	}
	void changeMode() { /* 공격모드로 변환한다 */ }
}

class Dropship extends Unit { // 수송선
	@Override
	void move(int x, int y) {
		System.out.println("Dropship[x=" + x + ",y=" + y + "]");
	}
	void load() { /* 선택된 대상을 태운다 */ }
	void unload() { /* 선택된 대상을 내린다 */ }
}

 

main 메서드에 아래와 같이 Object 배열로 선언되어 있다고 가정해보자.

Object는 모든 객체의 조상이기 때문에, 객체를 넣는 것에는 문제가 없다.

(= Object 타입의 리모컨으로 조정하려고 하는 건 문제없음)

 

하지만, for문을 돌리면서 move()를 실행하려고 보니, Object 타입의 리모컨에는 move()가 없다.

그래서 에러가 발생한다.

 

Object[] group = new Object[3];

group[0] = new Marine();
group[1] = new Tank();
group[2] = new DropShip();

// group의 타입은 Object[], group[i]의 타입은 Object
// group 배열의 객체들은 Object 객체로, Object 리모콘을 가짐
for(int i = 0; i < group.length; i++)
	group[i].move(100,200); 
	// Object에는 move()가 정의되어있지 않기 때문에 에러 발생

 

 

추상 클래스 사용하는 이유

1. 중복을 제거하여 설계도를 쉽게 작성

2. 코드 관리 용이 -> 변경이 쉽다. 부모 클래스만 바꿔주면 자식들도 알아서 바뀜.

 

 

14. 인터페이스

- 추상 메서드 집합

- 구현된 것이 전혀 없는 설계도. 껍데기

- 모든 멤버가 public

- 인스턴스변수, 생성자 등을 가질 수 없음. 오로지 추상 메서드만 ( + 상수, static 메서드, 디폴트 메서드도 있음 )

 

인터페이스의 모든 멤버 변수는 public static final이며, 생략 가능

인터페이스의 모든 메서드는 public abstract이며, 생략 가능

(그동안 인터페이스 사용하면서 메서드에 public abstract 붙이지 않았던 이유!!!)

 

interface 인터페이스명 {
	public static final 타입 상수이름 = 값;
	public abstract 메서드이름(매개변수목록);
}


interface PlayingCard {
	public static final int SPADE = 4;
	final int DIAMOND = 3; // public static final int DIAMOND = 3;
	static int HEART = 2; // public static final int HEART = 2; 
	int CLOVER = 1; // public static final int CLOVER = 1;
    
	public abstract String getCardNumber();
	String getCardKind(); // public abstract String getCardKind();
}

 

Q) 추상 클래스와 인터페이스의 차이

추상 클래스는 일반 클래스인데 추상 메서드를 가지고 있는 것

인터페이스는 추상 메서드만! 가지고 있는 것

 

 

15. 인터페이스의 상속

- 인터페이스는 인터페이스로부터만 상속받을 수 있다.

interface Movable extends object { } // 불가능

 

다중 상속이 가능

추상 메서드는 선언부가 같아도 어차피 내용이 없으니 충돌이 일어나지 않는다.

어차피 인터페이스를 상속한 클래스에서 해당 메서드를 구현해야하므로 어떤걸 써야할지 애매한 문제는 생기지 않는다.
왜? 본인이 구현한거라 본인 스스로가 알기 때문에!

반면에, 클래스가 다중 상속이 된다고 생각해보자.

A 클래스에서 print() 라는 메서드를 가지고 있고, B 클래스에서 print() 라는 메서드를 가지고 있을 때,

이 두 클래스를 상속받은 C 클래스는 c.print() 했을 때, 어떤 print()를 가져올지 헷갈리게 된다.

interface Movable {
	void move(int x, int y);
}

interface Attackable {
	void attack(Unit U);
}

interface Fightable extends Movable, Attackable { } // 다중 상속 가능

 

16. 인터페이스의 구현

- 인터페이스에 정의된 추상 메서드를 완성하는 것

- 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 함

 

class 클래스이름 implements 인터페이스이름 {
	// 인터페이스에 정의된 추상메서드를 모두 구현
}

 

Q) 추상 클래스와 인터페이스의 공통점

추상 메서드를 가지고 있다.

 

Q) 추상 클래스와 인터페이스의 차이점

인터페이스는 인스턴스 변수를 가질 수 없다.

추상 클래스는 추상 메서드, 인스턴스 변수, 인스턴스 메서드, 생성자 모두 가질 수 있다.

 

 

17. 인터페이스를 이용한 다형성

특정 인터페이스를 구현한 클래스 = 자식

특정 인터페이스 = 부모

즉, 특정 인터페이스를 구현한 클래스가 있다면, 인터페이스 타입으로 자식 클래스를 다룰 수 있다. 다형성 성립!

 

 

인터페이스 매개변수

메서드의 매개 변수가 인터페이스 타입이라는 것은

그 인터페이스를 구현한 자식 클래스의 객체만 들어오라는 것이다

 

class Fighter extends Unit implements Fightable {
	public void move(int x, int y) { /* 내용 생략 */ }
   	public void attack(Fightable f) { /* 내용 생략 */ }
}

// main
Unit u = new Fighter(); // 조상 타입 -> 자손 타입 다루기 가능 -> 다형성
Fightable f = new Fighter(); // 인터페이스 타입 -> 자손 타입 다루기 가능 -> 다형성

f.move(100, 200);
f.attack(new Fighter()); // Fightable 인터페이스를 구현한 클래스의 객체만 들어올 수 있음!!

 

인터페이스 리턴타입

메서드의 리턴 타입이 인터페이스 타입이라는 것은

메서드가 해당 인터페이스를 구현한 자식 클래스의 객체를 반환한다는 것이다.

 

Fightable method() { // 리턴 타입이 인터페이스(Fightable)
	Fighter f = new Fighter();
	return f; // Fighter가 Fightable 인터페이스를 구현했으니 반환이 가능한 것! -> 다형성
}

 

 

🔎 최종 예제

abstract class Unit { // 움직이고 멈추는 기능
	int x, y;
	abstract void move(int x, int y); // 같은 메서드라면  
	void stop() { System.out.println("정지"); };
}

interface Fightable { // 움직이고 싸우는 기능
	public void move(int x, int y);
	public void attack(Fightable f);
}

class Fighter extends Unit implements Fightable { // 인터페이스를 구현
	@Override
	public void move(int x, int y) {
		System.out.println("[" + x + "," + y + "]로 이동");
	}
	
	@Override
	public void attack(Fightable f) {
		System.out.println(f + "를 공격");
	}
	
	// 싸울 수 있는 상대를 불러옴
	Fightable getFightable() {
		Fighter f = new Fighter(); // Fighter를 생성해서 반환
		return f; // 형변환 자동으로 이루어짐
	}
}

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

		Fighter f1 = new Fighter();
		Fightable f2 = f1.getFightable();
		
		
		Unit u = new Fighter(); // 조상 타입으로 자손 객체를 다룸 -> 다형성!!
		Fightable fa = new Fighter(); // 인터페이스 타입으로 구현한 객체를 다룸 -> 다형성!!
		
		u.move(100, 200);
		u.stop();
//		u.attack(new Fighter()); // Unit에는 attack()이 없어서 호출 불가
		
		fa.move(100, 200);
		fa.attack(new Fighter()); // java7.Fighter@70dea4e를 공격
//		fa.stop(); // Fightable에는 stop()이 없어서 호출 불가
		
	}
}

 

18. 인터페이스의 장점

키보드를 살펴보자.

키보드는 뜯어보면 안쪽에 스프링, 고무, 판 등 여러 가지로 이루어져 있다.

만약 이런 날 것 상태로 키보드를 사용해야 한다면 너무 불편하겠지?

그래서 키캡을 통해 안쪽의 복잡한 부분을 가리고 사용자가 편리하게 사용할 수 있도록 해놨어.

키캡을 인터페이스라고 할 수 있다. 단지 껍데기!

 

컴퓨터를 살펴보자.

컴퓨터는 기계어를 알지 못하는 이상 직접적으로 다룰 수 없다.

그래서 GUI (Graphic User Interface) 환경을 통해 다룰 수 있게 해 놨는데, 이때 GUI가 인터페이스이다.

따라서, 컴퓨터 내부가 아무리 달라지고 업데이트되더라도, 우리는 그걸 신경 쓸 필요가 없고

그냥 GUI 환경 그대로 사용하면 된다. GUI는 껍데기라고 할 수 있다. 껍데기는 뭐다? 인터페이스!

쉽게 말해, 설계할 때는 귀찮지만 하나의 껍데기를 두어서 그 껍데기를 통해서 객체들을 다루면

객체들이 계속 늘어나도 껍데기는 고정이기 때문에 변경에 유리하다는 장점이 있다.

즉, 매개변수를 인터페이스 타입으로 해놓으면 여러 객체를 받을 수 있기 때문에 변경에 유리하다.

 

 

// A 클래스에는 변화가 없음 (변경에 유리)
class A { 
	public void method(I i) { // 인터페이스 타입으로 구현한 객체는 다 받음
		i.method();
	}
}

// B,C,D 클래스의 선언과 구현을 분리
interface I {
	public void method();
}

// B,C,D에서는 I 인터페이스를 구현해주기만 하면 됨
class B implements I {
	public void method() {
		System.out.println("B클래스 메서드");
	}
}

class C implements I {
	public void method() {
		System.out.println("C클래스 메서드");
	}
}

class D implements I {
	public void method() {
		System.out.println("D클래스 메서드");
	}
}

public class Main {
	public static void main(String[] args) {
		A a = new A();
		a.method(new D()); // new X() 부분만 변경해주면 된다! -> 변경에 유리
	}
}

 

 

1. 개발 시간을 단축할 수 있다.

위의 예에서 A 클래스는 I 인터페이스를 사용, B 클래스는 I 인터페이스를 구현하는 것을 말한다.

즉, 하나의 클래스 작업이 끝날 때까지 기다리는 것이 아니라 인터페이스를 중간에 둠으로써 동시에 개발이 가능하다.

 

2. 변경에 유리한 설계가 가능하다.

위의 예에서 A 클래스는 변경되지 않으면서, 매개변수를 인터페이스로 하여 인터페이스를 구현한 애들이라면 모두 받아줄 수 있다.

선언과 구현이 분리되어 있기 때문에 변경에 유리하고 독립적인 프로그래밍이 가능하다.

 

3. 표준화가 가능하다.

JDBC -> 자바에서 표준으로 지정해놓은 것 -> 인터페이스 집합

오라클, MySQL은 자바가 지정해준 JDBC 표준에 맞춰서 개발한다.

회사에서 오라클이 비싸져서 MySQL로 바꾸고 싶을 때 그들이 막 바꿔도 자바와의 연동에 문제가 없다.

왜? JDBC 표준에 맞춰서 개발되어 있기 때문에, 사용자는 바뀌든지 말든지 JDBC만 사용하던 대로 사용하면 된다.

 

4. 서로 관계없는 클래스들을 관계를 맺어줄 수 있다.

상속 계층도에서 공통점을 찾기 힘들 때, 같은 기능을 해야 해서 꼭 묶어줘야 한다면,

인터페이스를 하나 만들어서 구현해주면 된다. 인터페이스는 다중 상속이 가능하기 때문에!

 

 

"상속은 물려받는 것, 인터페이스는 장착하는 것"

하나 밖에 물려받을 수 없고, 장착은 무한대로 할 수 있다!

'JAVA' 카테고리의 다른 글

7장_2  (0) 2021.01.22
7장_1  (0) 2021.01.22
쓰레드  (0) 2021.01.20
다형성 / 동적 바인딩 / 인터페이스 / 추상클래스  (0) 2021.01.18
static은 언제 붙일까?  (0) 2021.01.14