관리 메뉴

엉망진창

추상클래스(abstract)와 인터페이스(Interface)를 사용하는 목적 본문

Study/Java

추상클래스(abstract)와 인터페이스(Interface)를 사용하는 목적

엉망진창 2008. 8. 25. 14:02
추상클래스(abstract)와 인터페이스(Interface)를 사용하는 목적 - http://dante2k.egloos.com/199169

##########################################################################
# 추상클래스(abstract)와 인터페이스(Interface)를 사용하는 목적 #
##########################################################################

작성자 : 장형화(hhjang97@venus.uos.ac.kr)
작성일 : 2005. 02. 14
수정일 :

원본 :
http://kin.naver.com/browse/db_detail.php?d1id=1&dir_id=10106&docid=923571
설명 :

추상클래스와 인터페이스 설명

덧붙임: http://dante2k.egloos.com/199169

################################# ################################# #################################


거칠게 말해서 확장성 때문입니다.

예를 하나 들어서 인터페이스를 쓸 경우와 안쓸 경우를 생각해보죠.

명함클래스(NameCard)와 출력하는 클래스(PrintNameCard)가 있고, NameCard는 print() 메소드가 있어서 스스로 출력도 되어야 한다고 생각해봅시다.

public class NameCard {
String name;
String phone;
String email;

PrintNameCard printNameCard;

public void setPrintNameCard(PrintNameCard p) {
this.printNameCard = p;
}
public void print() {
printNameCard.print( this );
}
}

public class PrintNameCard {
public void print( NameCard nc ) {
System.out.println("이름:"+nc.name);
}
}
===================================
이렇게 하면 되겠죠? 물론 생성자 등은 생략했네요. 암튼 NameCard 를 생성해서 print() 를 호출하면 이름만 출력되겠네요.

그런데 다른 사람이 와서, 이름과 전화번호, 이메일까지 모두 출력해달라고 한다고 하죠. 원래 이름만 출력해달라고 하는 사람도 물론 그대로 있구요.

그럼 새 클래스를 하나 출력해야겠죠.

public class PrintNameCard2 {
public void print( NameCard nc ) {
System.out.println("이름:"+nc.name);
System.out.println("전화번호:"+nc.phone);
System.out.println("이메일:"+nc.email);
}
}

그리고, NameCard 클래스가 이 클래스를 사용하도록 수정해야겠네요.

public class NameCard {
String name;
String phone;
String email;

PrintNameCard printNameCard;
PrintNameCard2 printNameCard2;

public void setPrintNameCard(PrintNameCard p) {
this.printNameCard = p;
}
public void print() {
printNameCard.print( this );
}
public void setPrintNameCard2(PrintNameCard2 p) {
this.printNameCard2 = p;
}
public void print2() {
printNameCard2.print( this );
}
}
===========================================

그럼 다른 요구사항을 하는 사람이 1000 명쯤 있다고 해봅시다.

PrintNamecard1000 까지 클래스를 모두 만들어야 하고, NameCard 안에는 SetPrintNameCard1000(); print1000(); 까지 모두 2000개의 메소드를 추가해줘야죠.

앞으로 더 몇 가지가 추가될지 모르는 상황이라면, 앞으로도 이 NameCard 클래스는 계속 수정돼야 할 것입니다.

출력방식에 따라서 PrintNameCard와 비슷한 클래스를 계속 만드는 것은 당연하지만, 문제는 NameCard 클래스가 계속 수정되어야 한다는 데에 있습니다.

메소드가 수천개씩 있는 클래스를 실행하는 것은 시스템에도 부담이 되는데다가, 무엇보다 클래스 소스를 계속 수정하는 것은 위험한 일이라는 것이죠.

하지만 Interface나 추상클래스를 쓰면 NameCard는 전혀 수정할 필요가 없도록 작성이 가능합니다.

public class NameCard {
String name;
String phone;
String email;

PrintNameCard printNameCard;

public void setPrintNameCard(PrintNameCard p) {
this.printNameCard = p;
}
public void print() {
printNameCard.print( this );
}
}

public interface PrintNameCard {
public void print( NameCard nc );
}


이것은 어떤가죠? NameCard는 PrintNameCard 라는 인터페이스만 사용을 했습니다. 그럼 PrintNameCard1,2,3,4 등의 클래스는 이 인터페이스만 구현해주면 되겠죠.
NameCard 에서는 PrintNameCard 라는 인터페이스를 받았지만, 실제로 setPrintNamecard() 메소드를 호출할때 넣어주는 객체는 그 인터페이스를 구현한 실제 객체가 되기 때문에 실행이 잘 됩니다.
게다가 그 안에 넣어주는 객체의 print()가 호출되기 때문에 출력형식도 마음대로 바꿔줄 수 있습니다. 단 그 객체들은 PrintNameCard 인터페이스만 구현해주면 되죠.

public class PrintNameCard1 implements PrintNameCard {
public void print( NameCard nc ) {
System.out.println("이름:"+nc.name);
}
}

public class PrintNameCard21 implements PrintNameCard {
public void print( NameCard nc ) {
System.out.println("이름:"+nc.name);
System.out.println("전화번호:"+nc.phone);
System.out.println("이메일:"+nc.email);
}
}

...

이런 식으로 얼마든 추가되어도 NameCard 클래스는 전혀 수정할 필요가 없습니다.

이것이 바로 인터페이스와 추상클래스를 사용하는 중요한 이유입니다. 추상클래스는 인터페이스 중에 몇몇 메소드를 미리 구현해둔 것이라고 보면 됩니다.





#################################
# 인터페이스와 추상클래스의차이점은?
#################################


1) 인터페이스는 다중 상속을 지원하나 추상 클래스는 단일 상속만 가능하다.
2) 추상 클래스에서는 메소드의 부분적인 구현이 가능하지만 인터페이스에서는 오직 메소드 선언만 있을 수 있다.




#################################
# 인터페이스와 추상클래스
#################################

§1 추상 클래스
추상클래스(abstract class)의 특성을 알아보기 위해 java.lang 패키지의 Number 클래스의 소스코드를 살펴봅시다.


package java.lang;
public abstract class Number implements java.io.Serializable {
public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
private static final long serialVersionUID = -8742448824652078965L;
}



§2 ■ package 선언
package 선언문
package java.lang;
을 통해 이 클래스가 java.lang 패키지에 속함을 알립니다.
■ 클래스 선언문
클래스 선언문
public abstract class Number implements java.io.Serializable
을 통해 이 Number 클래스는 공용public 접근의 추상abstract 클래스로 인터페이스 java.io.Serializable 를 이행implements 함을 알 수 있습니다.
인터페이스 이행문에 Serializable 인터페이스 명칭을 java.io.Serializable 로 완전조건명을 사용한 것에 주목하십시오. 만약 수입선언문


import java.io.Serializable;

를 사용했다면, 단순명을 사용해

public abstract class Number implements Serializable

로 표현해도 됩니다.


§3 ■ 왜 추상클래스인가?
중요한 것은 왜 Number 클래스를 추상abstract 클래스로 정의하였는가 하는 문제입니다.
그것은 수number 란 구체적인 대상이 아니라

Byte, Integer, Long, Short, Float, Double
등 구체적인 수를 통털어 지칭하는 집단명사이기 때문입니다. (아래 추상클래스와 인터페이스에 관한 Q&A 를 참조하십시오.)

■ 구성자 없음
추상클래스는 인스턴스화 될 수 없기 때문에, 구성자constuctor가 없습니다.


§4 ■ 추상 메쏘드
intValue(), longValue(), floatValue(), doubleValue() 등은 제한자 abstract 를 사용해 추상메쏘드로 정의하였습니다.
이들 추상메쏘드는 몸체 없이 선언문만 있는 것에 주목하십시오.
몸체도 없는, 따라서 이행도 없는 이러한 추상메쏘드가 무슨 소용이 있을까요?
추상클래스는 독립적으로는 아무 의미도 없습니다. 그 하부의 구체적인 인스턴스화 가능한 부분클래스의 초클래스로서 그 의미를 가집니다.

따라서 이 추상클래스가 포함하는 추상메쏘드들은 필연적으로 그 부분클래스에서 구체적인 이행이 정의될 것입니다. 이 점은 다음 시간에 알아봅니다.

그러니까 추상메쏘드는 그 부분클래스들이 공통적으로 가지는 행동이면서 구체적인 이행 방법(몸체)은 각기 특성에 맞게 달라지는 것들입니다.


§5 ■ 비추상 인스턴스 메쏘드
byteValue() 와 shortValue() 메쏘드는 추상메쏘드가 아닙니다.
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
이들은 각기 수 개체의 int 형 값을 구해 intValue() 이를 캐스트 (byte) 와 (short) 를 이용해 형변환 한 후 이를 되돌립니다.

추상클래스는 인터페이스와 달리 추상메쏘드 뿐 아니라 비추상메쏘드도 포함할 수 있음에 주목하십시오.

■ 필드변수
이 추상클래스 Number 는 정적변수
private static final long serialVersionUID = -8742448824652078965L;
를 포함합니다.
이는 private 로 본 클래스 안에서만 접근할 수 있음을 나타냅니다.
제한자 final 로 수정할 수 없음을 나타냅니다. 사실 이 정적변수는 상수 역할을 하지만, java 에서는 인터페이스에 포함된 필드만 상수로 부릅니다. 그러나 이는 다만 용어상의 문제이므로 신경쓸 필요없습니다.


t_1 다음 시험프로그램을 통해 추상클래스는 인스턴스화 할 수 없음을 확인합시다.
/* 추상클래스의 인스턴스화 불가성 */
class Test_13_2_1 {

public static void main(String[] args) {
Object num = new Number();
}
}

이를 컴파일하면 당연히 다음과 같이 인스턴스화 할 수 없다는 오류메시지가 나옵니다.

c:javafactory est>javac Test_13_2_1.java
Test_13_2_1.java:6: class java.lang.Number is an abstract class. It can't be instantiated.
Object num = new Number();


§6 인터페이스
■java.io.Serializable 인터페이스
앞의 Number 클래스는 java.io.Serializable 인터페이스를 이행하고 있습니다. 이 Serializable 인터페이스를 통해 인터페이스의 특성을 알아봅시다.


package java.io;
public interface Serializable {
static final long serialVersionUID = 1196656838076753133L;
}

이것이 Serializable 인터페이스의 소스코드 전부입니다. 도대체 이렇게 간단한 프로그램이 무슨 역할을 할까요?


§7 ■ package 선언
package 선언문
package java.io;
을 통해 이 인터페이스가 java.io 패키지에 속함을 알립니다.
■ 인터페이스 선언문
인터페이스 선언문
public interface Serializable
을 통해 이 Serializable 인터페이스는 공용public 접근의 인터페이스임을 지정합니다.
사실 별 다른 말이 없더라도 모든 인터페이스와 그 멤버는 public 입니다.
인터페이스는 이행이 없으므로, 제한자를 사용하지 않더라도 abstract 입니다.
대부분의 인터페이스 명칭은 이 Serializable 인터페이스처럼 "~할 수 있는"이라는 의미의 "~able"로 끝난다는 점에 주목하십시오. 이는 인터페이스가 다른 클래스들이 지닐 기능을 규정하는 데 사용된다는 점을 암시합니다.


§8 ■ package 선언
package 선언문
package java.io;
을 통해 이 인터페이스가 java.io 패키지에 속함을 알립니다.
■ 인터페이스의 역할
이 Serializable 인터페이스는 황당하게도 아무런 메쏘드도 포함하지 않습니다. 도대체 그렇다면 이 Serializable 인터페이스의 역할을 무엇일까요?
아래 추상클래스와 인터페이스에 관한 Q&A 에서 인터페이스의 용도를 "Adjustable, ItemSelectable, Transparency, Composite 등과 같이 형용사로 표현되는 속성이나, Shape, Stroke, Paint, PaintContext, CompositeContext, LayoutManager, LayoutManager2, ActiveEvent, PrintGraphics 등과 같은 추상명사로 표현되는 속성과 같이 개체와 상관없는 것을 공통적으로 규정하고자 할 때" 사용한다고 하였습니다.

이 Serializable 인터페이스는 이를 이행하는 클래스들이 직렬화 가능한(serializable) 기능을 갖게 합니다. 그러니까 앞의 Number 클래스형 개체는 직렬화 가능합니다.

직렬화 가능하다는 의미는 데이터를 주고받을 때 그 데이터를 직렬화할 수 있다는 의미로 데이터 입출력 스트림에 관한 기능입니다.


§9 ■ 상수

Serializable 인터페이스가 포함하는 유일한 멤버인 serialVersionUID 는

static final long serialVersionUID = 1196656838076753133L;
로 정의된 상수입니다.
이 상수에 접근제한자 public 이 없는데, 모든 인터페이스 멤버는 public 접근이므로, 실제로는 public 이 붙은 것과 동일하게 생각해야 합니다.
이 Serializable 인터페이스를 이행하는 Number 클래스에도 역시 동일한 명칭의 정적변수가
private static final long serialVersionUID = -8742448824652078965L;
로 정의되어 있습니다.
이와 같이 초클래스나 인터페이스에 정의된 동일한 명칭의 필드를 부분(이행)클래스에서 다시 정의하는 것을 필드 감추기hiding이라 합니다.


§10 ■java.lang.Comparable 인터페이스
또 다른 인터페이스로 다음 시간에 알아볼 Byte, Integer, Long, Short, Float, Double 등의 부분클래스들이 이행할 java.lang.Comparable 인터페이스를 알아봅시다.


package java.lang;
public interface Comparable {
public int compareTo(Object o);
}


이 역시 너무나 간단합니다. 단지 Serializable 인터페이스와 달리 메쏘드를 하나 포함합니다.


§11 ■ 메쏘드
Comparable 인터페이스가 포함하는 유일한 멤버인 compareTo() 메쏘드는
public int compareTo(Object o);
로 정의되어 있습니다.
이 메쏘드는 몸체가 없는 것으로 보아 추상메쏘드입니다. 그럼에도 불구하고 제한자 abstract 가 없습니다. 이와 같이 인터페이스의 모든 메쏘드는 별 다른 말이 없어도 추상메쏘드이므로 abstract를 붙일 필요가 없습니다.
이 추상메쏘드는 이 인터페이스를 이행하는 메쏘드에서 덮어쓰기를 통해 그 이행을 구체적으로 정의해야 합니다. 이의 예는 다음 시간에 알아봅니다.

■ Comparable 인터페이스의 용도
Comparable 인터페이스는 Serializable 인터페이스처럼, 이를 이행하는 클래스들의 개체가 비교가능한(comparable) 기능을 갖게 합니다.
비교가능하다는 것은 수처럼 순서가 있어 크기를 비교할 수 있다는 의미입니다.






#################################
# 인터페이스와 추상화 클래스의 차잇점이 무엇인가요?
#################################

일반적으로 자바의 허접 책들을 보면 인터페이스는 자바의 단일 상속에서 나오는
문제점을 해결하기 위해서 나왔다라고 나와있습니다. 추상화 클래스를 사용해서 상위 클래스를 상속하면 다른 상속을 할 수없기 때문에 나왔다라고 하고 예제로 몇개 들어 놓았습니다. 사용은 실제 그런 방식으로 합니다. 하지만 명백히 틀린 말입니다. 그것 때문에 나온것은 아닙니다. 객체지향 이론에 보면 인터페이스에 대한 이야기가 나옵니다. 즉,인터페이스는 타입을 재정의 하기위해서입니다.

클래스는 하나의 type 입니다. 인터페이스는 그 클래스가 가질수 있는 type을 오버로딩하기 위해서 만들어 진것입니다. 하나의 클래스가 여러 인터페이스를 구현하여 그 인터페이스 type으로 사용될수 있다는 것을 의미합니다.


반면 추상화 클래스는 상위 클래스에서 직접 구현을 하지 않고 하위 클래스에서 구현하게 함으로써 프로그램상에서 하위 클래스를 구현한 오브젝트가 각각 상위 클래스 타입으로 쓰일때 그 각각의 하위 오브젝트에 맞는 메소드를 동적으로 호출하기 위해서 사용합니다.

언듯 보면 매우 비슷하면서 그 사용도 같습니다. 하지만 분명한 점은 인터페이스를 구현하는 것은 이 클래스가 그 인터페이스 타입으로 사용될수 있다는 것을 컴파일러에게 알려주고 실행시간에 호출 되는 것이고 추상클래스는 실행시간에 동적으로 호출 된다는 것입니다.


## 추상 클래스 간단한 예제)

푸들 치와와 불독 ... 이런거 있죠 이걸 abstract class로 하면 dog이정도로요 ^^

그럼 dog를 상속받은 푸들은 개에 대한 속성이 있는거죠? 거기에 푸틀만의 속성이

더 있는거구요 ^^