자바 추상 클래스
목차
추상 클래스(abstract)는 인스턴스가 불가능한 클래스를 말합니다. 구체적인(concrete)의 반대말로써의 추상적인(abstract) 클래스는 객체 지향 프로그래밍의 설계방식입니다.
추상 클래스는 그 자체로는 사용이 안되고 sub 클래스가 상속을 받아 구현을 해줘야 합니다. 보통의 클래스 만으로도 충분히 프로그래밍을 구현할 수 있을텐데 왜 굳이 추상 클래스를 사용해야 하는가? 라는 의문이 당연히 생길 수 있습니다.
추상 클래스는 개별 프로젝트에도 사용할 수 있지만 범용 프레임워크를 만드는데 유용하게 사용할 수 있습니다. 그러니까 좀 더 규모가 크고 광범위한 개념을 포함하는 일반적인 객체 시스템을 설계하는데 사용됩니다.
아직도 좀 말이 어렵네요. 쉽게 말해 개인 프로젝트가 아니라 많은 인원이 참여하는 큰 프로젝트에 적합한 설계방식입니다. 또는 오픈소스 프레임워크를 만드는데 사용될 수 있습니다. JDK에서도 사용합니다. abstract 추상 클래스와 비슷하게는 인터페이스(interface)가 있는데 둘 다 직접 구현이 아니라 sub 클래스를 통한 확장(extends)과 구현(implement)으로 사용한다는 공통점이 있습니다.
그래서 혼자 프로그래밍 학습을 하다 보면 좀 잘 와닿지가 않습니다. 추상, 인터페이스 이런 용어들이 철학적으로 들리기 때문에 한층 더 혼동을 주는데요. 프로그래밍보다 인문학적 내용에 집중하는 것은 별로 도움이 되지 않습니다.
추상 클래스 예제
추상클래스는 abstract 키워드를 붙여서 만들 수 있습니다. abstract 이 붙으면 인스턴스를 만들 수 없습니다. 추상클래스의 메소드는 abstract 를 붙여서 구현하지 않아도 됩니다. 구현부는 sub 클래스에게 맡기는 겁니다. 이렇게 하는 이유는 설명한 것 처럼 좀 규모가 크거나 광범위한 개념을 사용하기 유리하기 때문입니다.
예를 들면 동물에는 네발동물이 있고 인간같은 두발동물이 있습니다. 추상적 동물이란 것은 인스턴스를 사용할 필요는 없으니까 abstract 로 놓고 sub클래스인 네발동물과 두발동물에 각각 이동하는 방법을 구현할 수 있습니다. 또 네발동물을 base 로 확장한 sub 클래스인 개와 고양이 클래스에서 상속을 받아 사용할 수 있습니다.
이런 추상 클래스의 개념적인 속성을 코드로 구현하는 것은 어떤가요? 코드안에는 동물이나 고양이가 있는게 아니라 메모리 안에서 클래스간의 관계가 설정될 뿐입니다.
그래서 어렵습니다. 이 포스팅은 어쩌면 추상클래스 그 자체 보다 왜 추상클래스가 어려운가 정도만 설명해도 충분할지 모르겠습니다.
상식적으로 동물과 고양이 인간의 관계도를 설정하는 것은 창조의 영역입니다. 어쩌면 다윈의 진화론을 믿느냐 신의 창조론을 믿느냐의 문제일 수도 있습니다.
그런 내용은 인류 문화의 신념인 특성을 갖기 때문에 프로그래밍 교육에서 다루기에는 한계가 있습니다만, 어쨋든 프로그래머는 추상클래스를 통해서 다른 종(class)간의 관계 등 시스템을 설계해야 합니다. 한가지 팁이 있다면 가급적 기술적인 요소를 우선하여 접근합니다.
동물과 네발동물의 관계 네발동물과 고양이의 관계에서 일반적 특성과 팩트를 뽑아내고 그것을 메모리 상에 모델링하고 알고리즘을 구현하는 것에 초점을 맞춥니다. 지난 학습부터 강조하고 있지만 프로그래머는 메모리와 컴퓨터 구동원리에 대하여 머리속에 나름의 모델을 이미지화할 수 있어야 합니다.
메모리의 이미지화 같은 것은 누가 가르쳐줄 수 있는게 아닙니다. 참고로 프로그래밍에 테크에 대해서는 IT선진국의 영문 웹사이트가 잘되있습니다. IT선진국은 미국을 중심으로 최근에는 수학의 나라 인도쪽도 상당히 잘 되있습니다. 전통 IT강국인 일본도 잘되어 있습니다. 이쪽의 웹사이트나 각종 유무료 강의에서 보면 그림들을 많이 활용합니다.
한가지 추천은 그림을 활용하건 뭐를 하건 머리속에 그 모델을 가져야 합니다. 프로그래밍을 짤 때 남의 코드를 보고 타이핑하는게 아니라 머리속의 이미지를 활용해서 만들수 있습니다. 프로그래밍도 Art(기술, 예술)의 영역이기 때문에 머리속의 이미지를 발달시킬 수록 더 장인과 같은 결과물을 만들 수 있습니다.
이제 예제를 통해서 살펴보겠습니다.
package com.kay; public class Main { public static void main(String[] args) { SubImplemented sub1 = new SubImplemented(); sub1.showInfo(); TopAbstract top1 = new SubImplemented(); top1.showInfo(); // abstract cannot be instantiated - * // topAbstract top2 = new topAbstract(); // cannot be instantiated even if implemented // AbstractClass ac1 = new AbstractClass(); } } abstract class TopAbstract{ public abstract void showInfo(); } class SubImplemented extends TopAbstract{ @Override public void showInfo() { System.out.println("*-this is sub implementation"); } } abstract class AbstractClass{ String description; AbstractClass(){ description = "default description"; } public void showInfo(){ System.out.println("description = " + description); } }
*-this is sub implementation *-this is sub implementation
abstract 클래스는 인스턴스 생성이 안됩니다. 그대신 메소드도 abstract 로 내용을 구현하지 않아도 됩니다. C의 함수 원형처럼 ; 세미콜론으로 막으면 됩니다. 컴파일러는 { } 이 블록을 기준으로 구현을 판단합니다. { } 이 아무 내용이 없더라도 컴파일러가 봤을 때는 이미 구현이 된 것이니 추상 메소드는 ; 세미콜론으로 만들어 줍니다.
위의 추상 메소드에는 매개변수가 없고 void 이지만 매개변수와 반환형까지 설정하면 됩니다.
main 함수에서 사용하는 방법은 일반 클래스와 같습니다. 다형성도 적용이 되는데요. 추상클래스가 base 클래스이므로 당연합니다. 메모리 적인 측면에서 바라볼 필요가 있습니다. abstract 의 메소드들은 구현이 안되어 있습니다. 이것을 sub 클래스가 받아도 코드가 없으므로 실행이 불가합니다. 따라서 인스턴스 생성전에 구현을 해줘야 합니다. 구현이 안되면 컴파일도 불가능합니다.
그 다음에 AbstractClass 를 보면 추상클래스임에도 내용을 다 구현해놨습니다. 다만 abstract 가 붙어있기 때문에 인스턴스의 생성은 불가능합니다. sub 클래스에 확장해야 인스턴스를 만들 수 있습니다. 추상클래스의 코드도 구현자체를 해놓는 것은 괜찮다는 말입니다.
한 가지 주의할 점은 abstract 클래스라도 static 메소드를 사용할 수 있습니다. 이런 부분은 문법적인 허용이므로 혼동할 필요가 없습니다. 위에서 설명한 것 처럼 추상 클래스를 개념적 철학적으로 접근하는 것은 도움이 별로 안됩니다. 기술적인 접근을 우선시 하는게 좋습니다.
추상클래스도 결국 인스턴스를 만들지 못할 뿐이지 메모리에 로드하는 것은 같으므로 static 메소드를 넣으면 인스턴스 없이도 메소드를 사용할 수 있습니다.
추상클래스의 핵심원리는 인스턴스를 생성할 수 없고, sub 클래스가 확장시 모든 메소드를 구현해야 한다는 점입니다. sub 클래스는 1계층 이상 아래로 계속 내려보낼 수 있습니다.
아래의 예제에서는 top – sub – sub2 로 두번 확장해서 내려갑니다. 이 경우에도 마지막 받은 sub2 클래스에서 사용하기 위해서 구현을 해야 합니다. implement 구현과 override 오버라이드 비슷하게 사용되는데요. implement 는 처음으로 구현할 때 override 는 기존에 있는 메소드를 밀어버릴 때 사용하는 용어입니다.
실제 메소드 구현시에는 상위 클래스를 보기 전에는 구분이 안가므로 계층도를 이미지로 파악하고 있도록 합니다.
public class Main { public static void main(String[] args) { SubAbstract2 sb2 = new SubAbstract2(); sb2.showInfo(); sb2.subAbstractShowInfo(); sb2.subAbstract2ShowInfo(); } } abstract class TopAbstract{ public abstract void showInfo(); } class SubImplemented extends TopAbstract{ @Override public void showInfo() { System.out.println("*-this is sub implementation"); } } abstract class SubAbstract extends TopAbstract{ public abstract void subAbstractShowInfo(); } class SubAbstract2 extends SubAbstract{ public void subAbstract2ShowInfo() { System.out.println("SubAbstract2 ShowInfo"); } @Override public void showInfo() { System.out.println("SubAbstract2 implemented Top overrided"); } @Override public void subAbstractShowInfo() { System.out.println("SubAbstract1 ShowInfo implemented"); } }
SubAbstract2 implemented Top overrided SubAbstract1 ShowInfo implemented SubAbstract2 ShowInfo
요약
자바 추상 클래스에 대해서 알아봤습니다. 위에서 제시한 것은 기본 문법이고 다양한 알고리즘으로 사용할 수 있기 때문에 실제 능수능란하게 사용하기 위해서는 여러가지로 시도를 해봐야 합니다.
추상클래스나 인터페이스도 마찬가지로 템플릿과 함께 프레임워크에 많이 사용합니다. 프레임워크는 어떤 앱에 특화한 툴을 만들어 주는 것 입니다. 기본적인 사양은 추상클래스에 일반 메소드를 구현해두고 사용자가 새로 정의할 부분만 추상 메소드로 남겨둡니다.
이런 방식은 윈도우 GUI 프로그램 등 에서 흔히 볼 수 있습니다. 윈도우는 똑같은 윈도우이기 때문에 운영체제의 동일한 코드는 기본으로 구현해두고 그 안의 내용과 이벤트 처리 등은 사용자들이 구현하도록 하는 방식입니다.
추상화(Abstraction) 기술은 컴퓨터 과학의 핵심 중 하나입니다. 또 일반인이 컴퓨터를 쉽게 사용할 수 있는 이유기도 합니다. 추상화 기술이 발달하지 않았다면 일반인도 컴퓨터 프로그래머도 다들 어렵게 컴퓨터를 사용하고 있었을 것 입니다.
외부참고문서
추상화(Abstraction)와 가상화(Virtualization) 컴퓨터구조