추상클래스란 미완성 설계도와 같다.
예를 들어보자.
모든 동물은 저마다 각자의 방식으로 운다. 우리가 기존의 방식대로 설계하면 dog.cry()
, cat.cry()
, tiger.cry()
와 같은 방법으로 각각의 동물의 울음을 구현할 수 있다. 그렇다면 여기에 상속의 개념을 더해 각각의 동물들이 Animal
이라는 클래스를 상속받는다고 하자. 그렇다면 Animal.cry()
는 어떻게 구현해야 하는가? 다르게 말하면 동물
은 어떻게 우는가?
그렇다. 정답은 모른다.
동물의 울음소리
는 각 동물마다 달라서 규정할 수 없다. 하지만 우리는 모든 동물은 운다.
라는 사실은 알고 있다. 이럴때 추상클래스를 사용한다. 추상클래스를 사용하면 동물의 울음소리
자체는 구현하지 않지만, 이 추상클래스를 상속받을때는 반드시 상속받은 동물의 울음소리를 구현해야 한다. 예를들어 Animal
클래스를 상속받은 Cat
클래스에서는 cry()
메소드를 반드시 구현해야 하며, 이 내용을 야용
으로 구현하는 것이다. 이렇듯 추상클래스는 상속받는 자식클래스의 공통분모를 만들기 위해 사용된다.
추상클래스는 클래스 앞에
abstract
을 붙여 정의한다.
추상클래스는 클래스 앞에 abstract
을 붙여 정의한다.
abstract class Animal{
}
이때, 추상클래스는 자체로 인스턴스를 생성할 수 없으며, 반드시 상속을 통해 선언을 해야 한다.
public class AbstractClass {
public static void main(String[] args) {
Animal animal= new Animal();// 오류! 추상클래스는 인스턴스 생성 불가!!
}
}
또한, 추상클래스는 뒤에서 배울 추상메소드
를 포함하고 있는 클래스를 의미한다. 이때, 중요한 것은 추상메소드를 포함한다
는 것이지, 모든 메소드가 추상메소드
일 필요는 없다.
추상메소드는 구현부가 없는 메소드를 의미한다.
모든 메소드는 선언부와 구현부로 나뉘어져 있다. 이때, 선언부는 메소드의 반환값, 메소드 이름, 매개변수가 선언부이며, 구현부는 실제 메소드의 수행 내용이다.
추상메소드는 선언부만 있는 메소드이다. 즉, 내용이 구현되 있지 않은, 메소드의 이름만 있는 메소드이다.
추상메소드는 메소드 앞에
abstract
을 붙여 선언한다.
추상메소드는 아래와 같이 메소드 앞에 abstract
키워드를 붙여 선언할 수 있다.
public abstract class Animal {
public abstract void cry();// 추상메소드
public abstract void eat();// 추상메소드
public abstract void sleep();// 추상메소드
}
주의할 점은 추상메소드를 선언할때 일반 클래스가 아닌 추상클래스 내에 선언해야 한다.
지금까지 배운 내용을 기반으로 간단한 동물 클래스를 작성해 보자.
먼저, 클래스는 다음과 같이 설계할 예정이다.
Animal
클래스를 각각 Dog
, Tiger
, Cat
, Lion
클래스가 상속받는 형식으로 작성할 예정이다.
먼저 Anmal
클래스를 추상클래스로 작성하면 다음과 같다.
Animal
public abstract class Animal {
public abstract void cry();// 추상메소드
public abstract void eat();// 추상메소드
public abstract void sleep();// 추상메소드
}
다음으로 각 클래스가 상속받는 부분을 작성하면 다음과 같다.
Dog
public class Dog extends Animal{
}
Tiger
public class Tiger extends Animal{
}
Cat
public class Cat extends Animal{
}
Lion
public class Lion extends Animal{
}
이제 각 상속받은 클래스에서 추상메소드를 작성해서 구현하면 다음과 같다.
Dog
public class Dog extends Animal{
@Override
public void cry() {
System.out.println("멍멍!!");
}
@Override
public void eat() {
System.out.println("강아지가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("강아지가 귀엽게 자고 있습니다.");
System.out.println("쿨쿨");
}
}
Tiger
public class Tiger extends Animal{
@Override
public void cry() {
System.out.println("어흥!!");
}
@Override
public void eat() {
System.out.println("호랑이가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("호랑이가 곤히 자고 있습니다.");
System.out.println("쿨쿨");
}
}
Cat
public class Cat extends Animal{
@Override
public void cry() {
System.out.println("냐용!!");
}
@Override
public void eat() {
System.out.println("고양이가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("고양이가 귀엽게 자고 있습니다.");
System.out.println("쿨쿨");
}
}
Lion
public class Lion extends Animal{
@Override
public void cry() {
System.out.println("으르렁!!");
}
@Override
public void eat() {
System.out.println("사자가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("호랑이가 곤히 자고 있습니다.");
System.out.println("쿨쿨");
}
}
다음으로 main메소드에서 만든 클래스들을 사용해보자.
public class AbstractClass {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat= new Cat();
Tiger tiger = new Tiger();
Lion lion = new Lion();
dog.cry();
cat.cry();
tiger.cry();
lion.cry();
dog.eat();
cat.eat();
tiger.eat();
lion.eat();
dog.sleep();
cat.sleep();
tiger.sleep();
lion.sleep();
}
}
output
멍멍!!
냐용!!
어흥!!
으르렁!!
강아지가 맛있게 밥을 먹습니다.
냠냠
고양이가 맛있게 밥을 먹습니다.
냠냠
호랑이가 맛있게 밥을 먹습니다.
냠냠
사자가 맛있게 밥을 먹습니다.
냠냠
강아지가 귀엽게 자고 있습니다.
쿨쿨
고양이가 귀엽게 자고 있습니다.
쿨쿨
호랑이가 곤히 자고 있습니다.
쿨쿨
호랑이가 곤히 자고 있습니다.
쿨쿨
정상적으로 동작함을 알 수 있다.
이제 각 동물의 고유한 특징을 추가해 보자.
강아지의 경우 애교를, 고양이는 꾹국이, 호랑이와 사자는 사냥하는 기능을 넣으면 다음과 같다.
Dog
public class Dog extends Animal{
@Override
public void cry() {
System.out.println("멍멍!!");
}
@Override
public void eat() {
System.out.println("강아지가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("강아지가 귀엽게 자고 있습니다.");
System.out.println("쿨쿨");
}
public void charming() {
System.out.println("강아지가 애교를 부립니다♥");
}
}
Cat
public class Cat extends Animal{
@Override
public void cry() {
System.out.println("냐용!!");
}
@Override
public void eat() {
System.out.println("고양이가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("고양이가 귀엽게 자고 있습니다.");
System.out.println("쿨쿨");
}
public void kneading() {
System.out.println("고양이가 꾹국이를 합니다.♥");
}
}
Tiger
public class Tiger extends Animal{
@Override
public void cry() {
System.out.println("어흥!!");
}
@Override
public void eat() {
System.out.println("호랑이가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("호랑이가 곤히 자고 있습니다.");
System.out.println("쿨쿨");
}
public void hunt() {
System.out.println("호랑이가 사냥을 합니다. 어릉!!");
}
}
Lion
public class Lion extends Animal{
@Override
public void cry() {
System.out.println("으르렁!!");
}
@Override
public void eat() {
System.out.println("사자가 맛있게 밥을 먹습니다.");
System.out.println("냠냠");
}
@Override
public void sleep() {
System.out.println("호랑이가 곤히 자고 있습니다.");
System.out.println("쿨쿨");
}
public void hunt() {
System.out.println("사자가 사냥을 합니다. 으르렁!!");
}
}
다시 main메소드에 해당 내용들을 추가하면 다음과 같다.
public class AbstractClass {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat= new Cat();
Tiger tiger = new Tiger();
Lion lion = new Lion();
dog.cry();
dog.eat();
dog.charming();
dog.sleep();
cat.cry();
cat.eat();
cat.kneading();
cat.sleep();
tiger.cry();
tiger.hunt();
tiger.eat();
tiger.sleep();
lion.cry();
lion.hunt();
lion.eat();
lion.sleep();
}
}
output
멍멍!!
강아지가 맛있게 밥을 먹습니다.
냠냠
강아지가 애교를 부립니다♥
강아지가 귀엽게 자고 있습니다.
쿨쿨
냐용!!
고양이가 맛있게 밥을 먹습니다.
냠냠
고양이가 꾹국이를 합니다.♥
고양이가 귀엽게 자고 있습니다.
쿨쿨
어흥!!
호랑이가 사냥을 합니다. 어릉!!
호랑이가 맛있게 밥을 먹습니다.
냠냠
호랑이가 곤히 자고 있습니다.
쿨쿨
으르렁!!
사자가 사냥을 합니다. 으르렁!!
사자가 맛있게 밥을 먹습니다.
냠냠
호랑이가 곤히 자고 있습니다.
쿨쿨
위 코드를 보면 알 수 있겠지만 변수가 4개나 있어서 관리가 어렵다. 이를 다형성을 이용하면 배열을 활용해 변수를 조금 더 쉽게 관리할 수 있다. 추상클래스 역시 상속관계에 있으므로 다형성이 가능하다.
다형성을 사용해 하나의 배열로 묶어 관리하면 다음과 같다.
public class AbstractClass {
public static void main(String[] args) {
Animal []animals= {
new Dog(),
new Cat(),
new Tiger(),
new Lion()
};
/*울기*/
for(Animal ani:animals) {
ani.cry();
}
/*먹기*/
for(Animal ani:animals) {
ani.eat();
}
/*고유기능*/
for(Animal ani:animals) {
if(ani instanceof Dog) {
((Dog) ani).charming();
}
else if(ani instanceof Cat) {
((Cat)ani).kneading();
}
else if(ani instanceof Tiger) {
((Tiger) ani).hunt();
}
else if(ani instanceof Lion) {
((Lion) ani).hunt();
}
}
/*잠자기*/
for(Animal ani:animals) {
ani.sleep();
}
}
}
output
멍멍!!
냐용!!
어흥!!
으르렁!!
강아지가 맛있게 밥을 먹습니다.
냠냠
고양이가 맛있게 밥을 먹습니다.
냠냠
호랑이가 맛있게 밥을 먹습니다.
냠냠
사자가 맛있게 밥을 먹습니다.
냠냠
강아지가 애교를 부립니다♥
고양이가 꾹국이를 합니다.♥
호랑이가 사냥을 합니다. 어릉!!
사자가 사냥을 합니다. 으르렁!!
강아지가 귀엽게 자고 있습니다.
쿨쿨
고양이가 귀엽게 자고 있습니다.
쿨쿨
호랑이가 곤히 자고 있습니다.
쿨쿨
호랑이가 곤히 자고 있습니다.
쿨쿨
이와같이 추상클래스에서 다형성을 사용하면 변수를 한번에 관리할 수 있어서 편리하다.
많은 도움이 되었습니다, 감사합니다.