본문으로 건너뛰기

인터페이스 잘 써보자

드디어 인터페이스를 반드시 써야 하는 상황이 발생!!

SpringBoot Batch로 집계정산을 담당하고 있다.
진짜 젤 첨에는 tasklet 으로 개발을 했는데
로직이 길기때문에 chunk 모델로 reader processor writer로 나눴다.(건수가 몇십만 건 이라는 이유도 있음)
프로세서 쪽이 계속해서 로직이 늘어났고
구조를 바꿨다.

프로세서에서 직접 dao를 호출하지 않고 service클래스를 만든후 processor -> service -> dao 식으로 말이다.

(RestApi에서 controller 가 service를 호출하고 service에서 dao를 호출하는 것처럼)

그런데 로직이 계속 늘어났다... 감당이 안된다...

뭔가 공통부분은 따로 뽑아내야하는 상황

그래서 IF를 만들라는 지시를 받았고, 무턱대고 들이댔다.
보통 프로젝트에서 xxxService implements ZZZZ 라는식으로 많이들 사용하긴 하는데
거의 관례적으로만 사용하는 케이스가 많아서
이렇게 직접 공통화 하는 작업을 위해서 인위로 인터페이스를 만들기 까지 해야하는 상황은 많지는 않을듯..

여튼.. 그러한 이유로 interface라는 놈이 어떤놈이지 다시금 살펴보고 자료를 찾아보았다.

인터페이스 내부의 메서드는 구현클래스에서 반드시 구현해야함

이런 인터페이스가 있고

public interface X {

int sum(int a, int b);
int average(int a, int b);

}

이렇게 구현체를 만들면 인터페이스 메서드를 반드시 구현해야함

public class A implements X {


@Override
public int sum(int a, int b) {
return 0;
}

@Override
public int average(int a, int b) {
return 0;
}
}

구현체가 여러개의 인터페이스를 구현가능함

이런 인터페이스 들이 있고

public interface X {

int sum(int a, int b);
int average(int a, int b);

}
public interface Y {

int multiply(int a, int b);
int divide(int a, int b);

}

구현하면 이렇게 된다.

public class A implements X, Y {


@Override
public int sum(int a, int b) {
return 0;
}

@Override
public int average(int a, int b) {
return 0;
}

@Override
public int multiply(int a, int b) {
return 0;
}

@Override
public int divide(int a, int b) {
return 0;
}
}

인터페이스도 상속이 된다.

X라는 인터페이스가 XX 인터페이스를 상속받고있다.

public interface X extends XX{

int sum(int a, int b);
int average(int a, int b);

}
public interface XX {

int minus(int a, int b);
}

여기서 인터페이스 X를 구현하면 이렇게 된다.

public class A implements X {


@Override
public int sum(int a, int b) {
return 0;
}

@Override
public int average(int a, int b) {
return 0;
}

@Override
public int minus(int a, int b) {
return 0;
}
}

abstract vs interface

추상클래스는 추상메서드(extends해서 override해야하는)도 갖을 수 있고, 구체적인 메서드도 갖을 수 있다
하지만 interface는 구체적인 메서드를 갖을 수 없다
자바는 다중상속이 안되기 때문에 abstract클래스 여러개를 다중상속 불가능하다, 하지만 인터페이스는 여러개를 구현할 수 있다

public class son extends mama, papa 불가능
public class son implements mama, papa 가능

인터페이스에는 변수와 값이 들어갈 수 있고, 구현체에서 그대로 사용가능

이런 인터페이스가 있고

public interface X {

double PI = 3.14;
String hello = "world";
int sum(int a, int b);
int average(int a, int b);

}

이렇게 구현했을때

public class A implements X {


@Override
public int sum(int a, int b) {
return 0;
}

@Override
public int average(int a, int b) {
return 0;
}
}

이런식으로 인스턴스화 해서 출력해보면, 인터페이스에서 정의한 변수의 값이 출력된다.

public class InterfaceApp {
public static void main(String[] args) {

A a = new A();

System.out.println(a.PI);
System.out.println(a.hello);


}
}
3.14
world

다형성(Polymorphism)

이런 인터페이스들이 있다.

public interface X {

int sum(int a, int b);
int average(int a, int b);

}
public interface Y {

int multiply(int a, int b);
int divide(int a, int b);

}

위의 두개를 구현하는 구현체가 있다.

public class A implements X, Y {


@Override
public int sum(int a, int b) {
return a+b;
}

@Override
public int average(int a, int b) {
return (a+b) / 2;
}

@Override
public int multiply(int a, int b) {
return a*b;
}

@Override
public int divide(int a, int b) {
return a/b;
}
}

그리고 인스턴스화 해서 메서드 실행을 확인하자

public class InterfaceApp {
public static void main(String[] args) {

A a = new A();

System.out.println(a.sum(2, 2));
System.out.println(a.average(3, 3));
System.out.println(a.divide(4, 2));
System.out.println(a.multiply(2, 6));
}
}
4
3
2
12

여기까지는 그냥 그러련히 ~~ 한다.

자자자... 근데 여기서 다형성이라는 것을 사용하면 어케되냐?

public class InterfaceApp {
public static void main(String[] args) {

X a = new A();

System.out.println(a.sum(2, 2));
System.out.println(a.average(3, 3));
System.out.println(a.divide(4, 2)); // 사용불가
System.out.println(a.multiply(2, 6)); // 사용불가
}
}

X형으로 A를 인스턴스화 했다. 그러면 Y형태의 메서드들은 사용불가
A라는애가 참조변수 a로서 사용될때는 X로써 사용된다 라는 뜻.

반대로

public class InterfaceApp {
public static void main(String[] args) {

Y a = new A();

System.out.println(a.sum(2, 2)); // 사용불가
System.out.println(a.average(3, 3)); // 사용불가
System.out.println(a.divide(4, 2));
System.out.println(a.multiply(2, 6));
}
}

Y형으로 A를 인스턴스화 했다. 그러면 X형의 메서드들은 사용불가!
A라는애가 참조변수 a로 사용될때, Y로써 사용된다 라는 뜻.

어떠한 구현체가 여러개의 수많은 인터페이스를 구현하고 있다 (public class Process implements A, B, C, D, E, F, G, H)
그러한 상황에서 A의 기능만을 갖는 객체로서 인스턴스를 하고 싶다면,(제한하고 싶다면)
A 참조변수 = new Process();

그러한 상황에서 C의 기능만을 갖는 객체로서 인스턴스를 하고 싶다면,(제한하고 싶다면)
C 참조변수 = new Process();

그러한 상황에서 G의 기능만을 갖는 객체로서 인스턴스를 하고 싶다면,(제한하고 싶다면)
G 참조변수 = new Process();

다형성에 대한 설명을 마져 다 하기에 조금더 남았다..

일단, 여기서 정리해볼것은
A 참조변수 = new Process(); A 라는것이 따지고보면 인터페이스명이 되는것이다.
C 참조변수 = new Process(); C 라는것이 따지고보면 인터페이스명이 되는것이다.

그런데.. ????

Y를 구현하는 B라는 애를 만들었다.

public class B implements Y {

@Override
public int multiply(int a, int b) {
return a*b;
}

@Override
public int divide(int a, int b) {
return a/b;
}
}

그리고 아까 인스턴스 하는 코드에서

public class InterfaceApp {
public static void main(String[] args) {

Y a = new B();

// System.out.println(a.sum(2, 2)); 사용불가
// System.out.println(a.average(3, 3)); 사용불가
System.out.println(a.divide(4, 2));
System.out.println(a.multiply(2, 6));
}
}

Y a = new B();
이 뜻이 뭐가 되냐면. Y라는 인터페이스랑 같은 인터페이스를 구현하는 클래스라면 어떤것이든 올 수 있다는 것이다.
즉.. Y라는 인터페이스를 구현하고 있는 어떤 클래스가 Play라는 클래스라면 Y a = new Play(); 이렇게 가능하다는 것이다. (호환성을 보장한다는 뜻임)

바로 이것이 변수이름 앞에다가 인터페이스를 지정하는 의미이고, 어떤 클래스가 데이터 타입을 무엇을 하냐에 따라서 다향한 얼굴을 갖을 수 있다.
이것이 다형성 이다.