아직 제대로 간단한 토이프로젝트도 시도해보지 못했지만 나름 자바의 환경이 슬슬 익숙해 질 때쯤, 자바를 공부한 친구가 인터페이스(interface)를 빨리 배우라고 추천을 해 줬습니다.
사실 Type Script에서 인터페이스를 어렴풋이 접해보았기 때문에, 문제 될 것이 없다고 생각했으나...
하루를 꼬박 걸려서 어렴풋이나마 인터페이스의 목적과 문법을 겨우 익혔습니다.
그런 상태에서 생각 정리차 적는 글이기 때문에
깊고 정확한 정보가 아닐 수 있습니다.
그래도 머리가 그닥인 제가 다시 보고 이해할 수 있게끔 쉽게 적어보자고 합니다.
사실 어떤 걸 먼저 익히는지는 상관 없는 것 같으나, 그래도 이 개념이 들어왔을 때 인터페이스가 완성 되는 것 같더라구요. 그래서 간단하게 먼저 정리합니다.
다형성 (Polymorphism)은 간단하게 하나의 객체 타입에 다양한 인스턴스를 참조 할 수 있는것 이라고 보면 됩니다.
사실 이게 왜 필요한지 모를수도 있겠는데요, 그러나 저도 타입스크립트를 쓰면서 한 두가지의 요소만 다른 인터페이스를 왕창 적다보니까 너무 복잡해 보였습니다.
때문에 객체에 다형성 (여러 타입을 참조 가능함)을 보장하여, 수십개의 타입이 선언되고 참조되는 복잡성을 방지하여 효율과 유지보수성을 높인다고 할 수 있겠습니다.
때문에 이 다형성을 위한 조건은 제 생각에 크게 한 가지 인데요,
예시를 보여드리겠습니다.
class Sum {
public int getSum (int a, int b) {
return a + b;
}
}
class SumUp extends Sum { // sum에 print 기능을 넣었다!
public void print(int a, int b) {
System.out.println(getSum(a, b));
}
}
쉽게 말해, Sum이 SumUp의 부모 클래스입니다.
이 때 객체에 어떤 인스턴스가 참조 가능할까요?
Sum sum1 = new Sum();
SumUp sum2 = new SumUp();
자세한 설명은 생략한다.
다르게 말하면, 부모 클래스는 자식 클래스를 참조 가능합니다.
Sum sum1 = new SumUp(); // 가능... 1
// SumUp sum2 = new Sum(); // 불가능...2
1번이 가능한 이유는 간단합니다.
참조할 SumUp에 Sum이 갖고있는 메서드(getSum) 을 포함하고 있기 때문입니다.
print 메서드가 실질적으로 참조되지 못한다 하더라도, 필요한 getSum은 갖고 있기에 오류 없이 가능합니다.
1번을 이해하면 2번이 불가능한 이유도 간단합니다.
getSum과 print 두 개의 메서드를 가진 객체 sum2에,
getSum 메서드 하나뿐인 Sum을 참조한다면 print는 참조할 값이 없기 때문입니다.
가 핵심입니다!
제목에서도 간단하게 (제가 이해한) 인터페이스의 목적을 정의하였지만
간단한 예시를 통해 더 쉽게 알아보겠습니다.
라고 요청하였다.
덧셈을 구하는 메서드는 아주 간단하지만, 여러분이 팀원들에게 좀 더 어려운 메서드를 요청했다고 가정해주세요 :)
아무튼 Kim, Lee, Park 세 명의 팀원은 각각 이러한 메서드를 구현했습니다.
class Kim {
public void sum (int a, int b) {
System.out.println(a + b);
}
}
class Lee {
public void plus (int a, int b) {
System.out.println(a + b);
}
}
class Park {
public double getSum (double a, double b) {
return a + b;
}
}
어... 제가 너무 두루뭉술하게 말했나요?
셋 다 틀리진 않았지만 모두 다른 메서드 를 작성했습니다.
때문에 이 메서드를 시험해보려고 하는데,
public class InterfaceApp {
public static void main(String[] args) {
Kim result = new Kim();
result.sum(1, 2);
result = new Lee(); // 에러: 같은 메서드를 포함하지 않기 때문에 오류가 난다.
result.sum(1, 2);
result = new Park(); // 에러: 여기도 마찬가지.
result.sum(1, 2);
}
}
이런... result라는 객체를 만들어서 시험해보려고 했으나
모두 다른 메서드 때문에 쉽게 테스트가 되지 않는군요.
이런 간단한 상황은 큰 문제가 아니지만, 어렵고 다양한 메서드를 가진 클래스를 부탁하거나, 외주를 맡겼다면 하나하나 일일히 비교해보기 쉽지 않겠군요...
그래서 이번엔 "인터페이스" 라는 기능명세서를 가지고 팀원분들께 부탁해 보았습니다.
interface CalSum {
int sum (int a, int b)
}
이 메서드의 머리(?)처럼 생긴 명세서를 해석하자면,
라는 명세서가 되겠군요.
위에서 보았듯, 저 이름, 인자, 리턴 타입 세 가지를 명확히 지정하여 다형성을 향상시키는 것이 인터페이스의 목적입니다.
아무튼, 이렇게 부탁했더니 이번엔 세 팀원들이 각각 이렇게 메서드를 구현했습니다.
class Kim implements CalSum {
public int sum (int a, int b) {
return a + b;
}
}
class Lee implements CalSum {
public int sum (int a, int b) {
int value = a + b;
return value;
}
}
class Park implements CalSum {
public int sum (int a, int b) {
System.out.println(a + b);
return a + b;
}
}
세 팀원 모두 약간씩 다르게 구현했지만 받는 인자와 이름, 리턴 타입 이라는 기준을 훌륭히 지켜주었군요!
때문에
public class InterfaceApp {
public static void main(String[] args) {
CalSum result = new Kim();
result.sum(1, 2);
result = new Lee(); // 같은 메서드를 포함하지 않기 때문에, 오류가 난다.
result.sum(1, 2);
result = new Park(); //여기도 마찬가지.
result.sum(1, 2);
}
}
이렇게 손쉽게 테스트 해 볼 수 있었어요!
이런 식으로 작성시, 주의사항이 한 가지 있는데, 바로 다형성의 법칙 입니다.
이는 처음에 result라는 객체를 만들 때, CalSum로 타입을 지정하고 Kim 인스턴스를 참조하였습니다.
Kim, Lee, Park에게 참조된 CalSum이라는 인터페이스는 이 클래스들 보다 요소가 적거나 같기 때문에 이렇게 참조 할 수 있습니다.
아무튼 이렇게 잘 따라준다면, 수 많은 요소를 가진 클래스를 요구하더라도 효과적으로 디버깅과 기능을 테스트 해 볼 수 있겠군요!
굿 잡!
인스턴스 안에 들어올 수 있는 요소들과 그 종류들을 간단하게 설명해보려고 합니다.
참고로, interface 일반적으로 외부 클래스에 참조되기 위해 만들어졌으므로
모든 접근제어자가 public 타입입니다.
[public static final] String TeamName = "Java";
// []괄호 안에 부분은 생략이 가능합니다.
말 그대로 고정된 상수값입니다. 굳이 implements 할 때 적지 않아도 자동으로 상속됩니다. 이 상수를 활용한 메서드를 짠다면 필요하겠군요.
[public abstract] String join(String name);
// 메서드 이름, 인자 타입과 갯수, 리턴 타입이 주어진다.
위에 팀원들에게 요청했던 메서드의 머리, 추상 메서드입니다.
이 이름과 인자, 리턴 타입을 가진 메서드를
꼭! 오버라이딩 (덮어쓰기) 해야 합니다. 그렇지 않다면 오류가 납니다!
[public] default void printName(String name){
System.out.println("안녕하세요 " + name + " 입니다");
}
앞에 default를 붙여서 선언해야하는 디폴트 메서드입니다.
이는 오버라이딩 해도 되지만, 안해도 해당 메서드 자체가 자동으로 상속 됩니다.
하거나 말거나 자유!
static void byebye(String name){ // default 대신 static을 붙이면 정적 메서드가 된다.
System.out.println("잘 가, "+ name + " !");
}
static을 붙여 사용하며, 상수처럼 고정된 메서드입니다.
이것 역시 따로 적지 않아도 그대로 상속되며,
정적 메서드는 인터페이스에서만 접근 가능합니다.
아무튼, 이러한 목적과 요소 외에도 Java에서 정적 메서드가 갖는 의미와 용도가 훨씬 더 큰것 같다.
그래도 이정도만 이해하고 나중에 더 자세하게 이해하고 정리해야지...
또, 여유가 된다면 Type Script의 인터페이스 까지 좀 더 공부하고 싶어졌다 :)
그럼 이만!