자바 인터페이스 기초
목차
자바 인터페이스는 추상 클래스와 비슷하기도 하고 다르기도 합니다. 차이점을 따지는 것은 좋지만 이 둘을 너무 분리시켜서 생각하면 헷갈리기가 쉽습니다.
인터페이스를 구성하는 키워드들의 의미에 집중하면서 학습하는 것이 좋습니다.
이 포스팅에서는 자바 인터페이스 기초를 알아보겠습니다.
인터페이스 예제
전체 인터페이스 코드를 쓰윽 보고 넘어갑니다. 각각 개별적으로 설명하겠습니다.
package com.kay; public class Main { public static void main(String[] args) { System.out.println("[인터페이스 정의와 구현]"); System.out.println("[-----------------------------]"); Draw d1 = new myDrawing(); d1.initialize(); d1.defaultInitialize(); d1.drawPoint(50,50); d1.drawRectangle(100, 100, 70, 70); d1.endDraw(); // cannot find symbol error // d1.showInfo(); // after typecasting, sub class method access myDrawing dc = (myDrawing)d1; dc.showInfo(); System.out.println("[-----------------------------]"); myDrawing d2 = new myDrawing(); d2.showInfo(); } } interface Draw{ // 상수, 변수가 아니다. String defaultColor = "BLACK"; int defaultSize = 2; public void initialize(); public void drawPoint(int posX, int posY); public void drawRectangle(int posX, int posY, int width, int height); public void endDraw(); // interface can't have body, just can declare // public void implementingInterface(){ // System.out.println("this cannot have a body"); // } default void defaultInitialize(){ System.out.println("... this can have a body - default"); currentStatus(); staticMethod(); } public static int rectangleSize(int width, int height){ return width * height; } private void currentStatus(){ System.out.println("... Draw interface implemented"); } private static void staticMethod(){ System.out.println("... this is private static method of Interface"); } } class myDrawing implements Draw{ int posX = 0, posY = 0; int width = 0, height = 0; @Override public void initialize() { System.out.println("...Initializing GDI Drawing"); // 인터페이스에 정의된 변수는 final 이다. // defaultColor = "BLUE"; } @Override public void drawPoint(int posX, int posY) { this.posX = posX; this.posY = posY; System.out.println("- Draw line at " + this.posX + ", " + this.posY); } @Override public void drawRectangle(int posX, int posY, int width, int height) { this.posX = posX; this.posY = posY; this.width = width; this.height = height; System.out.println("-- Draw Rectangle at " + this.posX + ", " + this.posY); System.out.println("-- width: " + this.width + " height: " + this.height); System.out.println("-- Rectangle size: " + Draw.rectangleSize(this.width, this.height)); } @Override public void endDraw() { System.out.println("...Return to the system"); } public void showInfo(){ System.out.println("this class implements Draw interface"); } }
[인터페이스 정의와 구현] [-----------------------------] ...Initializing GDI Drawing ... this can have a body - default ... Draw interface implemented ... this is private static method of Interface - Draw line at 50, 50 -- Draw Rectangle at 100, 100 -- width: 70 height: 70 -- Rectangle size: 4900 ...Return to the system this class implements Draw interface [-----------------------------] this class implements Draw interface
인터페이스 정의
인터페이스는 키워드 interface 로 정의합니다.
인터페이스에서 정의한 변수는 컴파일러가 public static final 키워드를 붙여서 모두 상수가 됩니다. 즉 인터페이스에 변수는 정의할 수 없습니다.
또한 인터페이스에는 메소드 헤드만 선언할 수 있습니다. 추상 클래스에서는 body 를 구현하는 것이 프로그래머의 자유였습니다. 그러나 interface 에서는 head 선언만 허용됩니다. (default 키워드 예외)
interface Draw{ // 상수, 변수가 아니다. String defaultColor = "BLACK"; int defaultSize = 2; public void initialize(); public void drawPoint(int posX, int posY); public void drawRectangle(int posX, int posY, int width, int height); public void endDraw(); }
이런 방식의 정의가 무엇을 의미하는지 생각해볼 필요가 있습니다. 우선 변수에 final 을 붙인다는 것은 인터페이스에는 변경이 불가능한 상수만 가져갈 수 있다는 뜻으로 불확실한 변수의 사용 가능성을 제거합니다.
또 구현을 하지 못하게 함으로써 좀 더 추상적인 클래스를 만들 수 있습니다. (interface 도 클래스의 일종입니다. 클래스와 완전히 다른 새로운 타입이 아닙니다)
정의를 이해하면 나머지는 키워드에 따라 가므로 쉽습니다.
인터페이스 클래스 구현
인터페이스는 클래스에서 구현할 수 있습니다. implements 구현한다는 키워드를 사용합니다. 클래스는 extends 이고 인터페이스는 implements 라는 단어의 의미에 차이가 있습니다.
클래스 상속시에는 확장하는 개념이고 인터페이스에서 구현하다는 것은 추상적 아이디어를 구현한다는 의미를 분명히 합니다. 어떻게 보면 추상클래스와 다를바가 없어 보이기도 하는데요. 자바에서는 프로그래머들에게 선택권을 준 것 입니다.
다만 인터페이스와 추상클래스의 가장 큰 차이는 클래스는 base 가 하나만 가능하지만 인터페이스는 여러개를 구현할 수 있습니다. 즉 다중상속(여러개의 인터페이스에서 상속받는 것)이 가능합니다.
실제 프레임워크를 사용하다 보면 하나 이상의 인터페이스를 구현하는 경우가 많습니다. 뭐 이것도 방법을 따지자면 여러개의 클래스를 상속하지 않아도 여러번 상속하면 비슷한 효과가 납니다만, 그렇게 하면 base 에서 확장하는 의미가 약해지고 훨씬 복잡한 설계가 되기 쉽습니다.
컴퓨터 프로그래밍은 점점 복잡해져가고 있지만 설계는 복잡한 것이 좋은 설계가 아닙니다.
자연의 원리는 매우 심플합니다. 컴퓨터도 자연의 원리안에서 돌아가는 것이므로 소프트웨어를 복잡하게 설계해봤자 컴퓨터만 개고생할 뿐입니다.
자바의 개발자는 객체 지향 프로그래머들이 클래스와 인터페이스 두개의 아이디어를 함께 사용하는게 더 낫다고 판단한 것 입니다. 조금 더 복잡하지만 이점이 있기 때문으로 볼 수 있습니다.
아래의 코드는 인터페이스를 구현한 것입니다. 기본적으로 추상 클래스를 extends 하는 것과 큰 차이는 없어 보일 겁니다. 인터페이스에 선언한 메소드들을 전부 오버라이드합니다. 인터페이스에 정의된 변수가 final 이기 때문에 혼동에 주의합니다.
class myDrawing implements Draw{ int posX = 0, posY = 0; int width = 0, height = 0; @Override public void initialize() { System.out.println("...Initializing GDI Drawing"); // 인터페이스에 정의된 변수는 final 이다. // defaultColor = "BLUE"; } @Override public void drawPoint(int posX, int posY) { this.posX = posX; this.posY = posY; System.out.println("- Draw line at " + this.posX + ", " + this.posY); } @Override public void drawRectangle(int posX, int posY, int width, int height) { this.posX = posX; this.posY = posY; this.width = width; this.height = height; System.out.println("-- Draw Rectangle at " + this.posX + ", " + this.posY); System.out.println("-- width: " + this.width + " height: " + this.height); System.out.println("-- Rectangle size: " + Draw.rectangleSize(this.width, this.height)); } }
default 메소드
인터페이스는 메소드의 body를 정의할 수 없다고 했습니다.
하지만 body를 정의할 필요도 있으므로 이런 경우 default 키워드를 사용하면 인터페이스에도 메소드를 정의할 수 있습니다. 이렇게 보다보면 원칙도 별로 없는 것 같이 보입니다만, 자바의 역사는 길기 때문에 다양한 경우의 수에 적응하면서 만들어진 결과입니다.
default void defaultInitialize(){ System.out.println("... this can have a body - default"); currentStatus(); staticMethod(); }
public static 메소드
static 메소드를 인터페이스에서도 정의할 수 있습니다. static 은 자바에서 좀 영역을 넘나드는 모습을 볼 수 있습니다. static 키워드를 사용하면 인스턴스가 생성되기 전에 인터페이스를 메모리에 로드하기 때문입니다. 불쑥 튀어나오는 듯한 static 의 사용법 자체가 예외적인 모습이죠.
public static int rectangleSize(int width, int height){ return width * height; }
private 메소드
private 메소드도 사용이 가능합니다. 인터페이스 내부에서만 돌아가는 코드입니다. private 에 static 도 붙일 수 있습니다.
인터페이스 내부에서 private 메소드는 default 메소드에서 호출가능하고 private static 메소드는 static 메소드에서 호출할 수 있습니다. 약간 복잡한 규칙이지만 키워드의 특성이 적용됩니다.
private 은 클래스 내부에서만 사용될 수 있는 접근제한자이며 인스턴스 생성후에 사용할 수 있습니다. static 은 인스턴스 생성 없이 사용할 수 있으므로 private 의 내부 static 메소드 간에 접근이 가능합니다.
설명이 약간 어려워보이지만 키워드의 본래 특성에 집중할 필요가 있습니다.
private void currentStatus(){ System.out.println("... Draw interface implemented"); } private static void staticMethod(){ System.out.println("... this is private static method of Interface"); }
main 함수의 코드
main 함수에서는 종합적인 코드를 확인할 수 있습니다.
인터페이스를 구현하는 클래스를 생성하는 개념은 같습니다. 여기서는 다형성의 특징에서 추상 클래스와 차이를 볼 수 있습니다. 추상 클래스는 base 클래스에서 다형성을 사용하면 sub 클래스의 자체 메소드에 접근할 수 있었습니다.
인터페이스에서는 타입캐스팅을 하지 않으면 sub 클래스의 자체 메소드에 접근이 되지 않습니다. 또 추상클래스와 혼동하게 되는 부분입니다.
public static void main(String[] args) { System.out.println("[인터페이스 정의와 구현]"); System.out.println("[-----------------------------]"); Draw d1 = new myDrawing(); d1.initialize(); d1.defaultInitialize(); d1.drawPoint(50,50); d1.drawRectangle(100, 100, 70, 70); d1.endDraw(); // cannot find symbol error // d1.showInfo(); // after typecasting, sub class method access myDrawing dc = (myDrawing)d1; dc.showInfo(); System.out.println("[-----------------------------]"); myDrawing d2 = new myDrawing(); d2.showInfo(); }
요약
자바 인터페이스 기초에 대해서 알아봤습니다.
추상 클래스와 비슷한 점이 많으면서 다르므로 차이점을 분명히 구분하는게 좋습니다.
인터페이스를 왜 사용하는가? 질문에 대한 해답은 주로 설계부분에 있으므로 이 한장의 포스팅에 모든 내용을 담는 것은 어렵습니다. 인터넷에는 요약된 내용들이 많이 있는데 방대한 내용을 추상적으로 설명하니 다들 어렵습니다. 초보자들이 이 단계를 뛰어넘기 위해서는 좋은 교재와 강의를 함께 듣는 방법을 추천합니다.
외부참고문서
Java Interface (w3schools.com)
자바 인터페이스 기초 – 유튜브
— 좀 쉽게쉽게 설명하는 것을 보는 것은 확실히 도움이 됩니다. 인터페이스 같이 개념이 잘 들어오지 않는 챕터에 대해서는 인터넷에서 관련 문서를 많이 읽어보고 유튜브 영상들을 시청하는게 도움이 됩니다.