Java Category/Java

[JAVA] 추상 클래스(Abstract Class), 추상 메소드(Abstract Method)

ReBugs 2023. 1. 9.

본 게시글은 혼자 공부하는 자바 (저자 : 신용권)의 책과 유튜브 영상을 참고하였고, 개인적으로 정리하는 글임을 알립니다.


사전적 의미로 추상은 실체 간에 공통되는 특성을 추출한 것을 말한다. 예를 들어 새, 곤충, 물고기 등의 실체에서 공통되는 특성을 추출해보면 동물이라는 공통점이 있다.

여기서 동물은 구체적인 실체라기보다는 실체들의 공통되는 특성을 가지고 있는 추상적인 것이라고 볼 수 있다.

추상 클래스

객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다. 추상 클래스와 실체 클래스는 상속의 관계를 가지고 있다. 추상 클래스가 부모, 실체 클래스가 자식으로 구현되어 실체 클래스는 추상 클래스의 모든 특성을 물려받고, 추가적인 특성을 가질 수 있다.

여기서 특성이란 필드와 메소드를 말한다.

 

추상 클래스의 용도

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

실체 클래스를 설계하는 사람이 여러 사람일 경우, 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있다.

데이터와 기능이 모두 동일함에도 불구하고 이름이 다르다 보니, 객체마다 사용 방법이 달라진다. 이때, 추상 클래스를 상속함으로써 필드와 메소드 이름을 통일할 수 있다.

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

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

예를 들어 추상 클래스인 전화 클래스가 있고 전화 클래스를 상속받은 실체 클래스인 휴대전화 클래스와 스마트폰 클래스가 있다고 하면 전화 클래스의 기능인 전화 송수신 기능을 선언하고 휴대전화 클래스와 스마트폰 클래스는 각각 자신만의 필드와 메소드를 작성하면 된다.

 

추상 클래스 선언

추상 클래스를 선언할 때에는 클래스 선언에 abstract 키워드를 붙이면 된다.

abstract를 붙이면 new 연산자를 이용해서 객체를 만들지 못하고, 상속을 통해 자식 클래스만 만들 수 있다.

추상 클래스도 일반 클래스와 마찬가지로 필드, 생성자, 메소드 선언을 할 수 있다. new 연산자로 직접 생성자를 호출할 수 없지만 자식 객체가 생성될 때 super를 호출해서 추상 클래스 객체를 생성하므로 추상 클래스도 생성자가 반드시 있어야 한다.

 

Phone.java

package TestPackage;
public abstract class Phone {
	//필드
	public String owner;
	
	//생성자
	public Phone(String owner) {
		this.owner = owner;
	}
	
	//메소드
	public void turnOn() {
		System.out.println("폰 전원 ON");
	}	
	public void turnOff() {
		System.out.println("폰 전원 OFF");
	}
}

 

SmartPhone.java

package TestPackage;
public class SmartPhone extends Phone {
	//생성자
	public SmartPhone(String owner) {
		super(owner);
	}
	//메소드
	public void internetSearch() {
		System.out.println("인터넷 검색");
	}
}

 

Test.java

package TestPackage;
public class Test {
	public static void main(String[] args) {
		//Phone phone = new Phone(); (x)
		
		SmartPhone smartPhone = new SmartPhone("홍길동");
		
		smartPhone.turnOn();
		smartPhone.internetSearch();
		smartPhone.turnOff();
	}
}
/*
폰 전원 ON
인터넷 검색
폰 전원 OFF
*/

 

추상 메소드

추상 클래스는 실체 클래스가 공통적으로 가져야 할 멤버를 정의해놓은 추상적인 클래스로, 실체 클래스의 멤버를 통일하는데 목적이 있다. 모든 실체들이 가지고 있는 메소드의 실행 내용이 동일하다면 추상 클래스에 메소드를 작성하는 것이 좋다.

하지만 메소드의 선언만 통일하고, 실행 내용은 실체 클래스마다 달라야 하는 경우가 있다. 예를 들어, 모든 동물은 소리를 내기 때문에 Animal 추상 클래스에서 sound()라는 메소드를 정의 했다고 가정하면, 어떤 소리를 내도록 해야하는데, 이것은 실체 클래스에서 직접 작성해야 될 부분이다. 왜냐하면 동물은 다양한 소리를 내므로 이것을 추상 클래스에서 통일적으로 작성할 수 없다. 그렇다고 해서 sound() 메소드를 실체 클래스에서 작성하도록 하면 sound() 메소드를 잊어버리고 작성하지 않을 경우 동물은 소리를 낸다는 것에 위배된다.

이런 경우를 위해서 추상 클래스는 추상 메소드를 선언할 수 있다.

추상 메소드의 강력한 기능중 하나는 추상 메소드가 선언되면 자식 클래스에서는 반드시 추상 메소드를 오버라이딩해서 실행내용을 작성해야한다. 오버라이딩하지 않으면 컴파일 에러가 발생한다.

 

Animal.java

package TestPackage;
public abstract class Animal {
	public String kind;
		
	public void breathe() {
		System.out.println("숨쉬는중 ");
	}

	public abstract void sound(); //추상 메소드 선언
}

 

Cat.java

package TestPackage;
public class Cat extends Animal{
	public Cat() {
		this.kind = "포유류";
	}

	@Override
	public void sound() { // 추상 메소드 오버라이딩
		System.out.println("야옹");
	}
}

 

Dog.java

package TestPackage;
public class Dog extends Animal{
	public Dog() {
		this.kind = "포유류";
	}

	@Override
	public void sound() { //추상 메소드 오버라이딩
		System.out.println("멍멍");
	}
}

 

Test.java

package TestPackage;
public class Test {
	public static void main(String[] args) {
			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("-----");
			
			//메소드의 다형성
			animalSound(new Dog());
			animalSound(new Cat());
		}
		
		public static void animalSound(Animal animal) {
			animal.sound();
		}
}
/*
멍멍
야옹
-----
멍멍
야옹
-----
멍멍
야옹
*/
  • Animal 변수로 타입 변환해서 sound() 메소드를 호출했다. 자식은 부모 타입으로 자동 타입 변환이 될 수 있고, 메소드가 오버라이딩되어 있을 경우 재정의된 자식 메소드가 호출되는 다형성의 특징이 그대로 적용된다.
  • 부모 타입의 매개 변수에 자식 객체를 대입해서 메소드의 다형성을 적용했다. 이것은 위의 내용과 같은 원리로 자식 객체가 부모 타입으로 자동 타입 변환되어 재정의된 sound()메소드가 호출된다.

 

댓글