[Java] 7-3 추상 클래스

객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 '추상 클래스'라고 한다.

추상 클래스(부모)와 실체 클래스(자식)는 '상속'의 관계를 가지고 있는데, 실체 클래스는 추상 클래스의 모든 특성(필드, 메소드)을 물려받고 추가적인 특성(재정의)를 가질 수 있다.

추상 클래스의 용도

1. 공통된 필드와 메소드의 이름을 통일할 목적

실체 클래스를 설계하는 사람이 여러 사람일 경우, 실체 클래스마다 필드와 메소드의 이름이 각자 다를 수 있다.

이런 경우에는 데이터와 기능이 모두 동일함에도 불구하고 이름이 서로 다르기 때문에 사용 방법이 달라진다.

따라서 이 방법보다는 공통적인 필드와 메소드를 추상 클래스에 선언하고, 실체 클래스에 추상 클래스를 상속함으로써 필드와 메소드 이름을 통일할 수 있다.

2. 실체 클래스를 작성할 때 시간 절약

공통적인 필드와 메소드는 추상 클래스인 Phone에 모두 선언해두고, 다른 점만 실체 클래스에 선언하면 실체 클래스를 작성하는 데 시간을 절약할 수 있다.

ex) 자동차를 설계할 때에는 공통적인 타이어 규격(추상 클래스)에 맞추어서 작성해야 한다. 타이어 규격을 준수하여 만들어진 한국 타이어나 금호 타이어(실체 클래스)는 추상 클래스를 상속한다고 볼 수 있다.

추상 클래스 선언

추상 클래스를 선언할 때는 클래스 앞에 abstract 키워드를 붙여야 한다.

abstract를 붙이면 new 연산자를 이용해서 객체를 만들지 못하고, 상속을 통해 자식 클래스만 만들 수 있다, 즉 부모 클래스로만 사용된다. (코드로 설명하자면 추상 클래스는 extends 뒤에만 올 수 있다.)

추상 클래스는 new 연산자로 직접 생성자를 호출할 수는 없지만 자식 객체가 생성될 때 super(...)를 호출해서 추상 클래스 객체를 생성해야 하므로 추상 클래스도 생성자가 반드시 있어야 한다.

package sec03.exam02;

//추상 클래스

public abstract class Phone {
	
	public String owner;
	
	public Phone(String owner) {
		this.owner=owner;
	}
	
	public void turnOn() {
		System.out.println("폰 전원을 켭니다.");
	}
	
	public void turnOff() {
		System.out.println("폰 전원을 끕니다.");
	}

}
package sec03.exam02;

//실체 클래스

public class SmartPhone extends Phone {

	public SmartPhone(String owner) {
		super(owner);
	}
	
	public void internetSearch() {
		System.out.println("인터넷 검색을 합니다.");
	}
	
}
package sec03.exam02;

//실행 클래스

public class PhoneExample {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		
		//Phone phone = new Phone() 생성자를 호출해서 객체를 생성할 수 X
		
		SmartPhone smartPhone = new SmartPhone("신우현");
		
		smartPhone.turnOn(); //Phone의 메소드
		smartPhone.internetSearch();
		smartPhone.turnOff(); //Phone의 메소드

	}

}

폰 전원을 켭니다.
인터넷 검색을 합니다.
폰 전원을 끕니다.

추상 메소드와 재정의

추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메소드들을 정의해놓은 추상적인 클래스이고, 실체 클래스의 멤버를 '통일'하는 데 목적이 있습니다.

이처럼 모든 실체들이 가지고 있는 메소드의 실행 내용이 동일하다면 추상 클래스에 메소드를 작성하는 것이 좋을 것이다.

하지만 메소드의 선언만 통일하고, 실행 내용은 실체 클래스마다 다르게 해야 하는 경우가 있다.

ex) 모든 동물은 소리를 내기 때문에 Animal 추상 클래스에서 sound()라는 메소드를 정의하되 동물들은 각자 다른 소리를 내므로 추상 클래스에서 통일적으로 작성할 수 없다. 따라서 추상 클래스는 '추상 메소드'를 선언할 수 있다.

추상 메소드는 abstract 키워드와 함께 메소드의 선언부만 있으며 메소드 실행 내용인 중괄호 {}가 없다.

이때 자식 클래스는 반드시 추상 메소드를 재정의하여 실행 내용을 작성해야 하는데, 그렇지 않으면 컴파일 에러가 발생한다.

package sec03.exam02;

//추상 클래스

public abstract class Animal {
	
	public String kind;
	
	public void breathe() {
		System.out.println("숨을 쉽니다.");
	}
	
	public abstract void sound(); //추상 메소드
	
}
package sec03.exam02;

public class Dog extends Animal{
	
	public Dog() {
		this.kind="포유류";
	}
	
	@Override //추상 메소드 재정의
	public void sound() {
		System.out.println("멍멍");
	}

}
package sec03.exam02;

public class Cat extends Animal {
	
	public Cat() {
		this.kind="포유류";
	}
	
	@Override
	public void sound() {
		System.out.println("야옹");
	}
	
}
package sec03.exam02;

public class AnimalExample {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		
        //일반적인 변수 호출 방식
		Dog dog = new Dog();
		Cat cat = new Cat();
		dog.sound();
		cat.sound();
		
		System.out.println("-----");
		
        //변수의 자동 타입 변환(자식->부모)
        //재정의된 자식 메소드가 호출됨(다형성의 특징)
		Animal animal = null;
		animal = new Dog();
		animal.sound();
		animal = new Cat();
		animal.sound();
		
		System.out.println("-----");
		
        //매개 변수에 자식 객체를 대입
        //자식 객체가 부모 타입으로 자동 타입 변환되어 재정의된 sound()메소드가 호출됨
        //메소드의 다형성
		animalSound(new Dog()); //자동 타입 변환
		animalSound(new Cat()); //자동 타입 변환

	}
	
	public static void animalSound(Animal animal) {
		animal.sound(); //재정의된 메소드 호출
	}

}

멍멍
야옹
----- 
멍멍
야옹
----- 
멍멍
야옹

좋은 웹페이지 즐겨찾기