관리 메뉴

HAMA 블로그

[옛날 Java] 인터페이스 vs 추상클래스 본문

Java

[옛날 Java] 인터페이스 vs 추상클래스

[하마] 이승현 (wowlsh93@gmail.com) 2016. 9. 14. 16:06


자바 8에서 인터페이스가 대폭 변경 되었기 때문이 아래 내용은 이제 구시대 유물이 되었다.

자바8로 개발을 시작하는 분들이면 읽지 말길 권유함.


질문 :

안녕하세요~

인터페이스와 추상클래스가 도대체 어떻게 다른 건지 궁금한데요.

구글링을 해보면 죄다 인터페이스의 특징, 추상클래스의 특징을 나열하면서

비교하는 글밖에 없는데, 이건 결과론적인 얘기인 것 같고요.

사실 추상클래스로도 인터페이스처럼 사용할 수는 있잖아요? 기술적으로 보면요.

그리고 상속을 사용하면 강결합이 발생해서 좋지 않다는데

구현도 마찬가지로 관계를 심어주긴 하니까 별 차이가 없다고 생각하고요.

그래서 제가 생각해본 결론은,

'만약 인터페이스가 없더라도 추상클래스로 동일하게 구현이 가능하지만(다중상속 허용 가정)

추상클래스는 그 자체로도 완전한 클래스를 만들 수 있는 설계도로서의 역할을 위해,

인터페이스는 단순히 행위만을 나타내는 명세서로서의 역할을 위해 둘을 구분지었다.

즉, 추상클래스를 적절히 사용치 못하였을 경우에 발생하는 위험을 피하고자

필요한 제약을 몇가지 추가하여 인터페이스를 만든 것이다'

이렇습니다.

간략히 말씀드리자면, '개발자의 편의를 위해 선택지를 추가하였다' 정도가 되겠네요.

인터페이스라는 것이 왜 나왔는지, 추상클래스와는 어떻게 다르게 써야하는지 생각해보다가

이 둘이 기술적으로 배타적인 관계가 아닌 듯하여 위와 같은 결론을 내려봤습니다.

제가 맞게 나불대는 건지 의견 부탁드릴게요. ㅋ



하마 :

추상클래스 = (단독객체생성불가, 구현가능) ,  인터페이스 = (단독객체생성불가, 구현불가) 로 언어창시자가 이렇게 룰을 정해놨으면 그거에 맞게 사용하면 됩니다. 굳이 어렵게 생각할 필요가 없다고 보네요.. 1.어떤 고정적인 흐름을 가진 템플릿이 있다고 확신한다.밑에 구현될놈들은 그 흐름을 사용할것이고 변화될 가능성이 희박하다. <-- 이러면 추상클래스를 써도 팀원들로부터 비판당하지 않겠고 2.어떤 고정적인 템플릿이 없다. 연결고리만 제공해서 유연성 강화. <--- 인터페이스 (대부분의 디자인패턴에 적용) 3.어떤 고정적인 템플릿이 있는데 ..클래스 내부에 고정시켜 놓기는 꺼림직하다. has-a 이다. <-- 이러면 인터페이스 + 템플릿인젝션 이렇게 상황에 맞게 변화되겠지요..


A님: 

Java 와 C# 언어는 다중 상속이 불가능한 언어입니다. (오류수정: 기존의 언어 중에서 다중 상속을 구현한 언어로 C++ 이 있는데, Java 와 C# 언어는 C++ 과 동일한 방식의 다중 상속을 제공하는 것은 아니라는 말입니다.) C++ 과 동일한 방식의 다중 상속은 설계하기도 어렵고 복잡한 문제가 많았던 것 같습니다. 그래서 언어를 설계할 때 다중 상속 기능은 아예 빼버렸던 것 같습니다. (오류수정: C++ 과 동일한 방식의 다중 상속을 도입하지 않았다는 뜻입니다. Java 와 C# 언어는 C++ 과는 다른 방식으로 다중 상속을 구현합니다.) 그런데, Java 와 C# 언어도 사용하다 보면, 다중 상속이 꼭 필요한 경우가 자주 있습니다. 그래서, 인터페이스를 이용하여 다중 상속을 흉내 낼 수 있게 만들었습니다. 글쓴이는 질문 글에서 “사실 추상클래스로도 인터페이스처럼 사용할 수는 있잖아요? 기술적으로 보면요.” 라고 말씀하셨는데, 그 말씀은 오직 단일 상속의 경우에만 그렇게 생각할 수 있습니다. 단일 상속만을 사용하는 경우에는, 인터페이스와 추상 클래스의 차이가 별로 느껴지지 않을 것입니다. 하지만, 추상 클래스는 다중 상속이 불가능하지만, 인터페이스는 가능하다는 점에서, 기술적으로 매우 다르게 구별됩니다. 예를 들어, Java 및 C# 의 기본 라이브러리들 중에는 하나의 클래스가 여러 인터페이스를 상속, 구현하는 경우가 자주 있습니다. 사실 인터페이스는 구현 코드를 갖지 않기 때문에 실제로는 다중 상속이 아니라고 생각됩니다. 그럼에도 불구하고 다수의 인터페이스를 상속, 구현함으로써 다중 상속과 비슷한 효과를 내고 있습니다. <추신> 추상클래스가 구현 코드를 가질 수 있고, 인터페이스가 구현 코드를 가질 수 없다는 것은 사실이지만, 추상클래스와 인터페이스를 직접 비교하는 것은 한마디로 엉터리 비교라고 생각합니다. 왜냐하면, 추상클래스와 인터페이스는 근본적으로 다른 목적으로 만들어졌기 때문에, 서로 상관이 없고, 따라서, 비교 자체가 잘못된 비교입니다. 비유를 들자면, 수지의 팔뚝과 나은이의 종아리를 비교하면서, 어디가 더 예쁘냐고 비교하는 것은 한마디로 엉터리 비교입니다. 왜냐하면, 팔뚝과 종아리는 완전히 다른 부위라서, 비교 자체가 논리가 성립하지 않습니다. 클래스의 상속-구현 개념은, 다수의 클래스가 공통된 속성이나 중복된 코드를 가질 때, 공통된 부분을 모아 하나의 상위 클래스로 통합하여 관리하는 것을 목적으로 설계된 개념입니다. 추상클래스에 '추상'이라는 부가적인 의미가 붙은 이유는, 일부 코드를 상위 클래스에서 구현하지 않고, 하위 클래스가 직접 구현하도록 강제하는 방법을 쓰기 때문에 붙여진 이름일 뿐, 추상클래스는 '클래스의 상속-구현'의 범주에 포함되는 개념입니다. 추상클래스도 상위클래스이기 때문에 당연히 구현 코드를 가질 수 있습니다. 반면에, 인터페이스는 다중 상속을 흉내내기 위한 도구로서 개발된 개념입니다. (오류수정: 다중 상속을 흉내낼 때 기존의 interface 라는 용어를 가져다 썼습니다. 그럼으로써, interface 라는 용어는 새로운 의미로 확장되었습니다.) Java 및 C# 언어는 C++ 방식의 다중 상속이 불가능한 언어이기 때문에 인터페이스는 구현 코드를 가질 수 없습니다. 추상클래스와 인터페이스는 비슷해 보이기는 해도, 근본적으로 서로 다른 개념이기 때문에, 이 둘을 직접적으로 비교하는 것은 엉터리 비교라고 생각합니다.


하마:

A님 // "인터페이스는 다중 상속을 흉내내기 위한 도구로서 개발된 개념입니다."<-- 저라면 면접에서 이렇게 말하면 0점. 다투고 싶진 않지만... 너무나 큰 오류라 ..(현재 다중상속에 너무 꽂히셔서 그런듯 싶네요..) 인터페이스 개념은 자바언어가 생겨나기 훨씬 전부터 있어왔고 그 용도는 다중상속과는 상관이 없습니다. 디자인패턴에서 왜 대부분의 패턴이 인터페이스를 가지고 있는가를 생각해보시기 바랍니다..


B님:그럼 한두마디로 표현한다면 어떤 표현이 좋을까요? 저도 개발은단지님 처럼 다중상속 가능 유무 차이 밖에 없다고 생각했었는데..


하마 :  한두마디로 표현한다라... 저는 "행위 템플릿" " 변경가능속성" 의 존재유무라고 보네요. 저 위에 제 글은 "행위" 측면에서 쓰여졌는데 읽어보시고요.. 변경가능한 속성들의 유무가 또 하나의 축이지요..( immutable 하냐 안하냐) 다중상속은 곁가지라고 봅니다..


질문자:

'처음부터 그렇게 만들어진 것이다'라고들 말씀하시는데, 전 그렇게 생각하지 않습니다. 일단, 다중 상속에 문제가 있어 이를 금지하고 대신 인터페이스로 해결하였다는 것은 제가 봐도 오류인 듯 싶습니다. 정말 다중 상속이 큰 문제였다면, 문법적인 제약을 두어 두 개의 구현된 메서드를 동시에 상속 받지 못하도록 할 수도 있을 겁니다. 굳이 인터페이스를 만들 필요도 없었겠죠. Holub은 "실용주의 디자인 패턴"이란 책에서 '디자인 패턴을 매우 일상적으로 사용하게 되면 이는 패턴이 아니라 이디엄이다' 라며, 일례로 C 언어가 주류일 당시 상속은 하나의 디자인 패턴이었으나 C++에 상속 기능이 언어에 내장된 이후로 이는 패턴이 아닌 이디엄이 되었다고 말을 합니다. Holub의 표현을 빌리자면, 현재 우리가 쓰는 인터페이스의 기능 역시 추상클래스를 이용한 하나의 패턴이었으며, 이것이 언어의 기능으로 들어오면서 상속처럼 특별한 패턴이 아닌 이디엄이 되었다고 볼 수 있습니다. (물론 이는 인터페이스가 추상클래스 보다 나중에 나왔다는 추측에 기반한 것입니다. 두 개념이 처음 고안된 시기를 찾아보려 했으나 실패를.. 혹 아시는 분은 알려주세요) 그 까닭은, 언어마다 추상클래스의 용법은 조금씩 다르겠지만 개념상 이를 인터페이스처럼 사용하는 것이 불가능하지는 않기 때문입니다. 하지만 인터페이스가 추구하는 바를 추상클래스만으로 이루려면 지켜야 할 제약이 생기고 위험 또한 생기기 마련입니다. 상속 기능 없이 상속을 구현한다는 것은 - 물론 저로선 상상조차 안되지만 - 정말 복잡하고 번거로운 일이겠지요. 마지막으로 C++의 '__interface'에 대한 msdn 문서에 다음과 같은 말이 있습니다. 'A C++ class or struct could be implemented with these rules, but __interface enforces them.' (http://msdn.microsoft.com/en-us/library/50h7kwtb)


하마 :

질문자 // " 인터페이스가 추구하는 바를 추상클래스만으로 이루려면 지켜야 할 제약이 생기고 위험 또한 생기기 마련입니다." <-- 맞습니다. 그래서 행위의 틀 과 immutable 한 속성만으로 제한해서 유연성을 극대화한것이 아시다시피 자바의 "interface" 입니다. 고슬링이 저렇게 만들어놨으면 저 의도에 충실하게 사용하면 된다는 거지요. "의도" 가 중요한데 그 의도 즉 나뉘어 지는 키 포인트가 "행위 템플릿" " 변경가능속성" 라는 얘기입니다. p.s 왜 저걸 사용하냐? 에 포커싱을 맞추는게 생산적일거 같네요.. 스마트포인터은 패턴이 아니라 이디엄이다. 이런거보다는 리소스 해제의 타이밍을 사용자가 직접 개입하는것은 위험성이 높아지기때문에 자동화 이런 "의도" 가 중요하겠지요. 홀럽이 좋아하죠? "의도"


질문자:

하마// 좋은 말씀 감사합니다. ㅎ 사실 님께서 '룰'에 대해 말씀하신 것도 '왜' 그 룰이 정해졌는지가 궁금한 거였거든요. 추상클래스로도 할 수 있는 것을 말이죠. 그러다 문득 책에서 읽었던 내용이 떠올라 접목해서 끄적여봤습니다.


A님:

하마// 귀하께서 말씀하시는 "자바언어가 생겨나기 훨씬 전부터 있어왔"던 인터페이스와 제가 말하는 인터페이스는 의미가 다릅니다. 같은 용어라도, 다른 분야에서는 다른 의미로 쓰이거나, 같은 분야에서도 다중적인 의미로 해석되는 경우가 많습니다. 예를 들자면, 소프트웨어 개발자가 말하는 "플랫폼"은 철도공사 업체가 쓰는 "플랫폼"과 같은 단어이지만, 전혀 다른 의미로 사용되고 있습니다. "인터페이스"라는 말도 소프트웨어 역사 속에서 그 의미가 계속 확장,변화되어 왔습니다. 다중 상속 기능을 언어에 구현하면서, 다중 상속 전용의 새로운 용어를 만들기보다는, 기존의 interface 라는 용어를 그대로 가져다 썼습니다. 왜냐하면, 다중 상속과 기존의 interface 는 그 핵심적 의미가 같으니까요. 가져다 쓰는 순간, interface 라는 용어는 그 의미가 확장,변화됩니다. 기존의 의미와는 다른(새로운) 의미를 갖게 됩니다. 제가 말하는 인터페이스는 디자인 패턴의 인터페이스도 아니고, COM 인터페이스도 아닙니다. 제가 말하는 인터페이스는 Java 및 C# 에서 다중 상속에 사용되는 인터페이스에 한정해서 말씀드렸습니다. 저는 "다른 것"을 말하고 있는데, 귀하께서는 "다른 것은 틀린 것"이라고 말씀하시는군요? 인터페이스가 "유연성"을 극대화한다고 볼 수 있는 이유는 당연하게도 구현 코드를 갖지 않기 때문입니다. 그러나, 추상클래스가 구현 코드를 갖는다는 이유로, 하위 클래스에게 "고정적인 흐름"을 강제하거나 "변화될 가능성이 희박"하다고 생각하지 않습니다. 추상클래스를 상속 받는 클래스는 기존의 구현된 코드를 override 해서 새로운 코드로 대체할 수도 있고, 아예 새로운 코드를 작성해서 다른 기능을 사용할 수도 있습니다. 추상클래스는 이미 추상멤버변수나 추상멤버함수를 통해 인터페이스와 동등한 수준의 유연성을 제공하고 있습니다. 추상클래스 또한 인터페이스 못지 않게 변화의 가능성이 충분하다고 생각합니다. 게다가, 인터페이스가 선언하는 멤버변수들(또는 멤버함수들) 또한 하위 클래스에게 "이런 규칙을 따르라"고 제약하기는 마찬가지입니다. 이런 관점에서는 추상클래스와 인터페이스의 차이점이 보이지 않습니다. 추상클래스를 비롯해서, 단일상속에 사용되는 클래스들은 "기존의 코드를 재사용할 때 얻는 효율성" 관점에서 보는 것이 타당합니다. 추상클래스를 "고정적인 흐름"이나 "변화 가능성의 제약"을 위해 사용한다고 생각하지 않습니다. 샤프란// "다중 상속에 문제가 있어 이를 금지하고 대신 인터페이스로 해결하였다는 것은 제가 봐도 오류인 듯 싶습니다" ==> 저는 다중 상속이 문제가 있어 "금지한다"고 생각한 적 없습니다. 현대적인 객체지향 언어가 다중 상속을 사용하지 않는다는 것이 가능하겠습니까? C++ 의 다중 상속은, 상위 클래스들이 모두 독자적인 구현 코드를 가질 수 있고, 하위 클래스가 그 모든 코드를 재사용할 수 있다는 점에서 매우 강력합니다. 그러나, Java 및 C# 언어는 C++ 언어와는 컨셉이 다른 언어이기 때문에, C++ 방식의 다중 상속을 그대로 도입하지 않았고, 그 대신 다른 방법을 이용하여 다중 상속을 구현하고 있습니다. 그 대신, Java 및 C# 에서는 "구현 코드가 없는" 클래스(선언만 갖는,인터페이스)들로부터만 다중 상속이 가능하도록 제한되었습니다. 강력함을 일부 포기하는 대신, 보다 쉽게 사용할 수 있게 한 것입니다. C++/Java/C# 등의 언어는 모두 다중 상속을 사용합니다. 제 말은 각각의 언어가 다중 상속을 구현하는 방법에 차이가 있다는 얘기입니다. "정말 다중 상속이 큰 문제였다면, 문법적인 제약을 두어 두 개의 구현된 메서드를 동시에 상속 받지 못하도록 할 수도 있을 겁니다. 굳이 인터페이스를 만들 필요도 없었겠죠" ==> 할 수도 있을 게 아니라 그렇게 했습니다. 즉, Java 및 C# 에서는 (단일상속을 제외한 추가적인)다중 상속 시 반드시 "구현 코드가 없는" 클래스로부터 상속받도록 제약을 두었습니다. "추상클래스로도 할 수 있는 것을 말이죠" ==> 인터페이스의 기능 중 하나인 다중 상속 기능은, 추상클래스로 할 수 있는 것이 아닙니다. 애초 원문글쓴이께서 추상클래스와 인터페이스가 별로 차이가 없다는 뉘앙스로 말씀하신 부분이 있기 때문에, 본인은 그 둘은 분명한 차이가 있다는 취지로, 추상클래스의 특징 중 하나인 단일상속과, 인터페이스의 특징 중 하나인 다중상속을 대비시켜서 말씀드린 게 제 이야기의 본질입니다.


질문자:

A답변자// 글 잘 보았습니다. 그런데 너무 다중 상속에만 치중하시는 건 아니신지요? 물론 저도 어떠한 추측에 기반하여 글을 썼지만, 님 또한 정확한 근거는 없으신 듯 합니다. 하마님께서도 말씀하셨지만 대부분의 디자인 패턴이 인터페이스를 활용한다는 것은 우연이 아닐 겁니다. 디자인 패턴에서 다중 상속 개념을 이용하는 것이 그렇게 많은가요? 다중 상속을 해결하고자 인터페이스를 만들었다면, 그 부분에만 쓰면 될텐데 왜 추상클래스 보다는 인터페이스를 지향할까요? 청출어람인가요? 추상클래스로는 얻기 힘든 이점들을 인터페이스를 이용하면 쉽게 얻을 수 있습니다. 추상클래스는 단일 상속만을, 인터페이스는 다중 상속을 지원한다는 것이 이 둘의 결정적인 목표는 아니라 생각합니다. p.s 주제넘지만, 하마님께서 말씀하신 '인터페이스'가 다른 의미인가요? 전 같아 보여서..


하마:

A답변자// "추상클래스의 특징 중 하나인 단일상속과, 인터페이스의 특징 중 하나인 다중상속을 대비시켜서 말씀드린 게 제 이야기의 본질입니다." <-- 문법적으로는 맞습니다. 틀리지 않습니다. 그 "차이" 는 분명합니다. 하지만 인터페이스(개념)가 추구하는 바를 추상클래스만으로 이루려면 지켜야 할 제약이 생기고 위험 또한 생기기 마련이라서 규제수준을 높혀서 유연성을 극대화하기 위해서 만들어진게 interface (자바문법) 입니다. 의도가 이거란 말입니다. "의도"를 봐야지 "차이" 중 하나에 포커싱을 맞추는게 틀린건 아니지만 아쉽다는 얘기였습니다.. 

p.s 저런식의 규제 혹은 특정모형을 통해서 소프트웨어를 더 좋게 만드는경우가 매우 많지요. 일례하나만 들면 객체지향개발에서 쓰레드를 직접사용하면 다양하게 요리될수있으나 그 위험성이 크기에 규제를 주고 특정 모델을 만든게 Actor 모형이라고 합니다. akka 라는 라이브러리가 그 구현체이고.. 큰 틀에서보면 이런거지요..

Comments