5. 기초_추상클래스
일반적으로 하나의 추상 메소드를 가지고 있는 클래스를 추상 클래스
추상 메소드는 선언부만 있고 구현부가 없는 메소드(해당 메소드는 서브 클래스에서 반드시 구현)
인스턴스화 할 수 없으며, 일반 클래스처럼 메서드와 필드를 가질 수 있음
abstract class Animal {
// 일반 메서드
public void eat() {
System.out.println("This animal is eating.");
}
// 추상 메서드
public abstract void makeSound();
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
추상 클래스 VS 일반 클래스
추상 클래스나 일반 클래스나 공통되는 변수나 메서드를 묶어서 만들 수 있고, 상속받아 사용할 수 있다. 그러나 일반 상속(일반 클래스를 상속)과는 다르게, 추상 클래스는 반드시 구현을 필요로 하는 추상 메서드가 존재할 수 있으므로, 모든 하위 클래스에게 구현을 강제할 수도 있습니다. 따라서, 추상 클래스는 객체를 생성할 수 없습니다. 반드시 하위 클래스를 통해서만 객체를 생성할 수 있습니다.
추상클래스는
객체로 생성될 수 없는 클래스로 자식 클래스에서 확장할 수 있도록 만들어진 클래스입니다. 즉, 미완성의 설계도라고 할 수 있으며, 추상 클래스에는 추상 메소드와 일반 메소드 모두 포함시킬 수 있으며, 인스턴스 변수도 가질 수 있습니다. 보통 기본적인 구현은 추상 클래스에서 제공하고, 하위 클래스에서는 고유의 동작을 확장하기 위해서 사용합니다.
이에 반해 인터페이스는 스펙이 정의된 메소드들의 집합입니다. 인터페이스를 구현하는 클래스는 반드시 이 메서드들의 동작을 정의해야 하며, 해당 클래스는 동일한 사용 방법과 동작을 보장할 수 없습니다. 이를 통해 다형성이 가능해집니다. 인터페이스는 보통 클래스 간 상속 관계가 없는 다른 클래스가 동일한 메소드를 구현해야 할 때 사용되는데, 때로는 단순히 클래스 타입을 구분짓기 위해 사용되기도 합니다.
추상 클래스는 반드시 추상 메서드를 갖는가?
항상 그렇지는 않습니다. 추상 클래스는 인스턴스화 할 수 없는 클래스를 의미하며, 일반 메소드만 포함할 수도 있습니다. 이를 통해 공통 기능을 상속받게 할 수 있습니다.
추상 메서드를 갖는 클래스는 반드시 abstract 제어자를 붙여야 하며, 이를 추상 클래스라고 부릅니다. 반대로 추상 클래스가 추상 메서드를 반드시 가져야 하는 것은 아닙니다.
추상 클래스를 쓰는 이유는?
미완성의 설계도라고 할 수 있겠습니다. 인터페이스를 대신 쓸 수도 있지만, 인터페이스에서는 선언부만 있기 때문에, 이를 구현한 하위 클래스에서는 공통적인 기능이 중복되더라도 모두 중복적으로 구현부에 작성을 해야합니다.
공통 기능의 상속
추상 클래스는 하위 클래스들이 공통적으로 사용할 수 있는 메소드와 속성을 정의하며 코드의 중복 줄이고 유지보수성을 향상
하위 클래스의 일관성 유지
추상 클래스는 하위 클래스들이 반드시 구현해야 하는 메서드를 정의할 수 있습니다. 이를 통해 일관된 인터페이스를 유지할 수 있습니다.
인스턴스화 방지
인스턴스로 만들 수 없기 때문에, 반드시 하위 클래스를 통해서만 객체를 생성할 수 있습니다. 이는 특정 클래스가 직접 사용되지 않도록 강제하는데 유용합니다.
추상 클래스에 정의한 일반 메소드를 하위 클래스에서 재정의해서 사용할 수 있는가?
하위 클래스에서 오버라이딩해서 사용할 수 있습니다. 이는 객체지향 프로그래밍에서 다형성을 활용하는 중요한 방법 중 하나입니다. 그러나, 장/단점이 있을 수 있고, 장점보다는 단점이 더 많다고 할 수 있습니다.
추상 클래스의 생성자
추상 클래스는 new 연산자를 통해서 인스턴스화가 불가능하지만, 생성자를 아예 사용하지 못하는 것은 아니다. 추상 클래스내에 생성자 메서드를 정의하고, 자식 클래스에서 super() 키워드를 통해 생성자 메소드를 호출 할 수 있다.
//추상 클래스 선언
public abstract class Pet {
public int age;
public Pet(int age){
this.age = age;
}
public abstract void walk();
}
//서브 클래스 선언
public class Dog extends Pet{
public String name;
public Dog(int age, String name){
super(age);
this.name = name;
}
@Override
public void walk(){
// ...
};
}
추상 클래스에서의 final 키워드
메서드의 동작 보장
final 메서드를 사용하면, 상위 클래스에서 정의한 메서드의 동작을 하위 클래스에서 변경할 수 없으므로, 메서드의 일관된 동작을 보장할 수 있습니다.
설계의 명확성
final 메서드를 사용하여 하위 클래스에서 변경할 수 없는 메서드를 명확히 정의함으로써, 상위 클래스의 설계 의도를 분명히 할 수 있습니다.
성능 최적화
컴파일러는 final 메서드를 호출할 때, 해당 메소드가 오버라이딩 되지 않음을 알기 때문에, 메서드 호출 에 대한 최적화를 할 수 있습니다.
public abstract class Animal {
// final 메서드는 하위 클래스에서 오버라이딩할 수 없음
public final void sleep() {
System.out.println("This animal is sleeping.");
}
// 추상 메서드 - 하위 클래스에서 구현해야 함
public abstract void makeSound();
}
여기서 final은 추상 클래스에서 이미 정의된 메서드를 하위 클래스에서 오버라이딩 할 수 없도록 제어합니다. 하위 클래스에서 변경할 수 없도록 보장하여, 이를 통해 메소드의 일관된 동작을 유지할 수 있습니다.
public final class Utility {
public static void doSomething() {
// Implementation
}
}
// 컴파일 오류: Utility 클래스는 final로 선언되어 상속할 수 없음
// public class ExtendedUtility extends Utility {
// }
부모 클래스를 추상 클래스로써 이용해야 하는 이유
자식 클래스를 업캐스팅해서 다형성을 이용해야 할 필요가 있을 수 있기 때문입니다.
public class Test1 {
public static void main(String[] args) {
Unit[] group = new Unit[3];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new DropShip();
for(Unit u : group) {
u.move(100, 200);
}
}
}
위의 코드에서는, Unit의 자식 클래스들의 인스턴스들을 부모 클래스 타입으로 묶었습니다. 업캐스팅을 통해서 Unit 배열에 자식 객체들을 할당 할 수 있게 됩니다. 그리고 반복문을 통해서 move 메서드를 실행시킵니다. 다형성을 이용한 효율적인 코드를 구성할 수 있습니다.
공통된 필드와 메서드를 통일하는 목적으로는 일반 클래스로도 가능하여, 꼭 추상 클래스만의 고유 용도라고 할 수 없습니다. 그러나 인터페이스는 똑같이 안에 필드를 선언할 수는 있지만, 자동으로 public static final 처리가 되어 공통 상수가 됩니다.
자식 클래스가 반드시 추상 메소드를 구현하도록 만들기 위함입니다.
규격에 맞는 설계 구현
안드로이드 앱 만들 때
안드로이드 SDK에서 제공하는 추상 클래스를 상속받아 필요한 추상 메소드를 구현
Last updated