추상 클래스는 추상 메서드를 포함한 클래스를 말하고 추상 메서드는 구현부(body)가 없이 선언부만 있는 메서드입니다. 여기서 선언부는 반환값, 메서드 이름, 매개변수를 만합니다.
추상 클래스는 abstract 예약어를 사용하고, new 생성자를 사용해 인스턴스화 할 수 없습니다. 인스턴스를 만들었을 때 메서드에 body가 없기에 해당 추상메서드를 호출해도 실행될 수 있는 부분이 없기 때문입니다.
//5번
public abstract class Computer {
//2번
public void testing() {};
//3번
public void display();
//4번
public abstract void typing();
//1번
public void turnOn() {
System.out.println("컴퓨터 전원을 켭니다");
}
public void turnOff() {
System.out.println("컴퓨터 전원을 끕니다");
}
}
1번 : trunOn/Off 메서드는 우리가 평소에 만들던 메서드로 선언부와 구현부가 모두 구현되어 있습니다.
2번 : testing()은 중괄호'{}'안에 내용이 생략되어 있어 추상메서드라고 생각하기 쉽지만 추상메서드에는 중괄호 자체가 생각략됩니다. 그래서 추상메서드를 만들기 원한다면 중괄호를 지워줘야 합니다.
3번 : display()는 선언부만 존재하는 메서드기 때문에 추상메서드의 조건을 만족하지만 에러가 발생합니다. 그때 4번 typing()과 같이 반환타입 앞에 abstract를 붙여주면 추상메서드로 인식을 하고 에러가 발생하지 않습니다.
5번 : 클래스안에 추상메서드가 생성됨과 동시에 5번 클래스 선언부에도 에러가 발생하는데 추상메서드와 마찬가지로 class 앞에 abstract를 붙여줘서 추상메서드를 포함한 추상 클래스인 것을 알려주면 에러가 사라지게 됩니다.
구현부가 없는 추상메서드를 작성하는 이유는 추상 클래스를 상속받는 하위 클래스에서 개별적으로 작성되야 하기 때문입니다. 위의 예제에 turnOn과 turnOff는 상속관계 전체에서 공통되게 사용될 수 있는 메서드이지만 display와 typing은 전체적으로 필요한 메서드이지만 각 개별 하위 클래스에서 작성될 내용입니다.
정리해보면 추상클래스는 상위클래스 역할을 하고 상속관계내에서 전체적으로 필요하지만 개별적인 작성이 필요한 메서드를 추상메서드로 만들어 구현부를 하위클래스에서 작성하도록 하는 것 입니다.
//1번
public class Desktop extends Computer {
//2번
public void display() {
System.out.println("Desktop Display");
}
public void typing() {
System.out.println("Desktop Typing");
}
}
1번 : Computer에 하위클래스를 만들기위해 Desktop 클래스를 작성하고 extends를 하면 Desktop에 에러가 발생합니다. 에러메세지에 커서를 올려보면 구현되지 않은 메서드를 구현하던지, 해당 클래스를
abstract로 선언하라고 메세지가 뜹니다. 만약 상속받은 2개의 추상메서드 중 하나만 구현하고 싶다면 여전히 해당 클래스에는 추상 메서드가 있다는 의미이기 때문에 abstract 클래스로 선언해야 합니다.
2번 : 에러 메세지에서 메서드를 구현하기를 클릭하면 추상메서드들이 자동으로 구현됩니다. 비어있는 구현부를 위와 같이 채워줍니다.
위의 그림은 클래스간의 관계도로 클래스는 네모로 표시하고 안에 줄을 긋고 그 클래스명과 내부 요소들을 구분합니다.
추상클래스나 메서드는 이텔릭체로 표현합니다. Computer는 2개의 추상메서드를 가지고 있고 하위 클래스들에게 모두 상속됩니다
DeskTop처럼 모든 추상메서드를 구현한 클래스는 보통의 클래스가 되지만 NoteBook처럼 일부의 추상메서드만 구현한 클래스는 추상클래스가 됩니다.
public abstract class NoteBook extends Computer {
@Override
public void typing() {
System.out.println("NoteBook Typing");
}
}
나머지 클래스들을 구현하기위해 NoteBook 클래스를 작성합니다. 해당 클래스에서는 typing()만 작성할 것이기 때문에 abstract 클래스를 선언해줍니다.
public class MyNoteBook extends NoteBook {
public void display() {
System.out.println("MyNoteBook Display");
}
}
NoteBook 클래스에 하위 클래스인 MyNoteBook 클래스를 작성합니다. 상위 클래스에서 이미 typing()메서드를 작성했기에 하위클래에서는 display()만 작성하면 abstract클래스가 아닌 온전한 클래스가 됩니다.
public class ComputerTest {
public static void main(String[] args) {
//1번
//Computer computer = new Computer(); //에러발생
//computer.display(); //표현할 내용없음
//2번
Computer computer = new Desktop();
computer.display(); // DeskTop Display
computer.turnOn(); // 컴퓨터 전원을 켭니다.
//3번
Computer computer2 = new MyNoteBook();
NoteBook computer3 = new MyNoteBook();
1번: 테스트를 위해 ComputerTest 클래스를 작성하고 Computer 인스턴스를 생성하려고 하면 에러가 발생합니다. .display()같이 추상메서드가 있다면 표현할 내용이 없기 때문입니다. 그래서 추상클래스는 생성을 하지 못합니다.
2번: 상위 클래스인 Computer를 타입으로해서 Desktop인스턴스를 생성합니다. 그후 .display를 실행하면 당연히 Desktop클래스의 .display()가 실행됩니다. 업스케일을 하면 개별 하위 클래스에서 작성된 메서드는 상위 클래스 타입으로 생성된 인스턴스에서 사용이 불가하지만 이렇게 추상메서드로 작성해두면 상위 클래스를 자료형으로 해도 하위클래스에서 작성된 메서드를 실행할 수 있습니다.
또한 TurnOn과 같이 상위 클래스에서 작성된 메서드는 당연히 상위클래스를 자료형으로 하는 인스턴스에서 사용할 수 있습니다.
3번 : MyNoteBook 클래스는 두개의 상위 클래스 중 어떤 것이든 타입으로 하여 인스턴스를 생성할 수 있습니다.
추상 클래스는 주로 상속의 상위클래스로 사용되어 하위 클래스가 구현해야할 추상 메서드를 포함하고 있습니다.
이는 상위클래스를 타입으로하여 하위클래스의 인스턴스를 만들 때 상위클래스에 구현되지 않은 메서드도 사용할 수 있게 하여 여러개의 하위클래스의 인스턴스를 하나의 자료형으로 관리 할 수 있다는 장점이 있습니다.
추상 클래스에서 구현된 메서드는 하위클래스에서 공통으로 사용할 수 있고 필요에 따라 하위 클래스에서 재정의 될 수 있습니다.