오늘은 자바의 다향성, 추상클래스, 그리고 인터페이스에 대해 공부했다. 이게 어제 공부했던 클래스의 상속에서 더 들어가는 내용이다. 상속의 발전이냐하면... 그거는 아닌거같고 그냥 기능이라고 생각하겠다.
난 말을 참 못한다.
다형성은 여러개의 형태를 갖는다는 의미로, 객채지향 프로그래밍의 3대 특징 중 하나이다.
저번에 객체지향 프로그래밍으 3대 특징에대해 서술했는데, 다시 한번 적어보자면 다음과 같다.
- 캡슐화
- 상속
- 다형성
이러면 필자는 이제 객체지향 프로그래밍의 3대 특징에 대해 공부를 했다고 말할 수는 있겠다. 와!
근데 잘하는건 아니니까 더 열심히 코드를 짜보고, 내걸 만들어야겠다.
각설하구 다형성이라는 것은 상속을 이용한 기술로, 자식객체를 부모클래스 타입의 변수로 다룰 수 있는 기술이다. 무슨 뜻인지는 찬찬히 정리해보자. 나도 뭔소린지 잘 몰랐다.
예를들어 Monitor라는 클래스가 있다고 가정해보자 그리고 그 클래스 안에는 모니터의 성질들이 들어있다고 가정해보자.
public class Monitor{
private boolean power;
private int volume;
private int size;
}
그러면 이 Monitor라는 클래스를 상속해 모니터의 기본 성질들을 가져오고, 상속해온 자식 클래스에서는 그 클래스만의 성질을 더 추가할 수 있을 것 이다.
public class LGMonitor extends Monitor{
public String gajuenEnLG;
}
-------------------------------------------
public class SamsumgMonitor extends Monitor{
public String hajimanSamsungGgirlSuuji;
}
이렇게 생성한 클래스를 이용해서 타입은 Monitor의 타입이지만, 객체는 다른 클래스에서 생성하는 것이 가능하다.
Monitor LGM = new LGMonitor();
Monitor SM = new SamsungMonitor();
부모타입의 변수에 자식타입의 객체를 저장하는 것 이다.
하지만 반대의 경우로 자식타입의 변수가 부모타입객체를 저장할 수 없다.
LGMonitor LGM = new Monitor(); //이게 안된다.
지금 위에서 한 객체생성 과정이 up casting이다. 솔직히 이부분은 내가 이해한게 제대로됐는지 확신이 안선다... 틀렸으면 나중에 수정해야겠다.
public class Monitor{
private boolean power;
private int volume;
private int size;
}
public class LGMonitor extends Monitor{
public String gajuenEnLG;
}
Monitor LGM = new LGMonitor();
이와 같은 코드의 경우, LGM 객체는 LGMonitor의 객체로 생성되었지만, 타입은 Monitor타입이기 때문에, LGMonitor의 문자열 gaJuenEnLG를 사용할 수 없으며, Monitor객체에 있는 변수와 메소드만 사용이 가능하다.
up casting의 경우에는 참조한 객체에만 있는 변수와 메소드를 사용할 수 없었다.
그러나 down casting으로 형변환을 시켜준다면 참조한 객체의 변수와 메소드를 사용할 수 있다.
Monitor LGM = new LGMonitor();
((LGMonitor)LGM).gajuEnenLG;
down casting을 이용해 객체를 불러내어 자료를 사용하는 것이 가능하다.
클래스간의 형 변환은 반드시 상속관계에 있는 클래스끼리만 가능하다.
추상클래스는 뜻한다. (요거를)
- 몸체가 없는 메소드(추상 메소드)를 포함한 클래스
- abstract 키워드가 선언부에 달린 클래스
public abstract class Main{
private int num;
private String str;
public abstract void chu();
}
코드를 보면 Main클래스 안에있는 chu메소드의 내용이 비어있다. 이는 추상 클래스를 상속한 때 오버 라이딩(상속해서 가져갈때 메소드를 수정해서 사용하는 것)을 강제하는데 그 목적이 있다.
public class test extends Main{
}
위와 같은 코드를 작성하면 Main클래스가 덜 완성된 추상 클래스이기 때문에 오류가 난다.
public class test extends Main{
@Override
public void chu(){
System.out.println("오버 라이드")
}
}
오버 라이드를 통해 완성된 코드로 바꿔주면, 클래스가 정상적으로 작동될 것 이다.
추상 클래스의 특징을 적어보고 넘어가보자.
- 미완성 클래스(abstract 키워드 사용)로 자체적으로 객체 생성이 불가능하다.
-> 반드시 상속하여 객체를 생성한다.- abstract 메소드가 포함된 클래스는 반드시 abstract 클래스이다.
그러나 abstract메소드가 없어도 abstract 클래스를 선언하는 것은 가능하다.(객체 생성 방지)- abstract 메소드 외에 일반 메소드와 변수를 포함할 수 있다.
- 객체 생성은 안되나, 참조형 변수 type으로는 사용이 가능하다.(다형성으로는 가능하다.)
상수와 abstract메소드만으로 이루어진 추상클래스의 변형체이다. 메소드의 통일성을 부여하기 위해서 abstract 메소드만 따로 모아 놓은 것으로, 상속 시 인터페이스에 정의된 모든 메소드를 구현해야 한다. 그러니까 아까 본 추상 클래스가 추상 메소드와, 일반 정의된 메소드, 변수 등이 섞여있다고 하면, 인터페이스는 모든 메소드가 추상 메소드인 것 이다.
[접근제어지시자] interface 인터페이스명{
}
public interface Monitor{
public static final boolean power=true;//변수는 상수형변수만 가능
public void powerOn();
//모든 메소드가 abstract메소드이므로 abstract생략이 가능
}
인터페이스를 사용하는 방법이었다.
인터페이스의 특징과 장점을 정리해보자.
인터페이스의 특징
- 인터페이스의 모든 메소드는 묵시적으로 public이고 abstract이다.
- 변수는 public static final이다.
- 객체 생성은 안되나, 참조형 변수로서 가능하다.(다형성으로는 가능)
인터페이스의 장점
- 상위 타입의 역할로 다형성을 지원하여 연결해주는 역할을 수행 가능하다.
- 해당 객제가 다양한 기능을 제공한다해도, 인터페이스에만 해당하는 기능만을 사용할 수 있도록 제어하는것이 가능하다.
- 공통 기능상의 일관성을 제공하고, 공동작업을 위한 가이드라인이 제공된다.
인터페이스를 사용하는 이유를 계산기 툴을 만드는 작업을 예로 들어보겠다.
public interface CalcInterface {
//더하기
public int add(int num1,int num2, int num3);
//빼기
public int minus(int num1, int num2);
//곱하기
public int multiple(int num1, int num2,int num3);
//나누기
public double div(int num1, int num2);
}
이것이 인터페이스다. 위의 코드를 보면, add, minus, multiple, div의 네가지 메소드를 만들려는 것을 확인할 수 있다. 그리고 이 메소드의 알고리즘을 구현하는 사람이 A, 실제 실행 하는 메인 메소드를 구현하는 사람 B가 있다고 해보자.
public class CalcImpl implements CalcInterface{
@Override
public int add(int num1, int num2, int num3) {
return (num1+num2+num3);
}
@Override
public int minus(int num1, int num2) {
return (num1-num2);
}
@Override
public int multiple(int num1, int num2, int num3) {
return (num1*num2*num3);
}
@Override
public double div(int num1, int num2) {
return ((double)num1/num2);
}
이것이 계산기 인터페이스를 받고 계산해주는 알고리즘을 짠 모습이다. 굉장히 간단하게 매개변수들을 더하고, 빼고, 곱하고, 나눴다. 지금은 굉장히 쉬운 예를 들었지만, 이것이 구현하는데 시간이 걸린다고 생각해보자. B는 A가 만든 알고리즘을 사용해 실행파일을 만들 것 이다. 그런데 A가 본인의 일을 하는동안, B는 아무것도 못하게된다. B가 써야할 것을 A가 만들고있으니까. 그래서 인터페이스가 필요한 것 이다. B 또한 인터페이스를 받았다.
public class CalcMain {
private Scanner sc;
private CalcImplTest calc;
public CalcMain() {
super();
sc = new Scanner(System.in);
calc = new CalcImplTest();
}
public void main() {
System.out.println("===== 계산기 =====");
System.out.println("1. 더하기");
System.out.println("2. 빼기");
System.out.println("3. 곱하기");
System.out.println("4. 나누기");
System.out.println("0. 종료");
System.out.print("선택 > ");
int sel = sc.nextInt();
switch(sel) {
case 1:
break;//case 1 done
case 2:
break;//case 2 done
case 3:
break;//case 3 done
case 4:
break;//case 4 done
case 0:
return;//case 0 done
default:
break;//default done
}//switch end
}//main end
그리고 B는 계산기를 돌아가게 하는 메인메뉴를 만들었다. 이제 B는 더하기 실행 메소드부터 만들어나가기위해 인터페이스를 참고했고, 더하기 메소드에 들어갈 숫자가 몇개인지 매개변수의 갯수를 통해 알 수 있었다. 이를 통해 B는 A가 잘 구현해줄 것을 믿고 임시로 메소드 Test클래스를 만들어 실행하는 클래스를 만들어가기 시작했다.
public class CalcImplTest implements CalcInterface{
@Override
public int add(int num1, int num2, int num3) {
return -1;
}
@Override
public int minus(int num1, int num2) {
return -2;
}
@Override
public int multiple(int num1, int num2, int num3) {
return -3;
}
@Override
public double div(int num1, int num2) {
return -1.0;
}
}
return타입만 맞춰서 임의의 값을 넣은 후, 실행 클래스를 만들고, 제대로 굴러가는지 확인했다.
public void add() {
System.out.print("첫번째 숫자 입력 : ");
int num1 = sc.nextInt();
System.out.print("두번째 숫자 입력 : ");
int num2 = sc.nextInt();
System.out.print("세번째 숫자 입력 : ");
int num3 = sc.nextInt();
int result = calc.add(num1, num2, num3);
System.out.println("결과 : "+result);
}//add end
public void minus() {
System.out.print("첫번째 숫자 입력 : ");
int num1 = sc.nextInt();
System.out.print("두번째 숫자 입력 : ");
int num2 = sc.nextInt();
int result = calc.minus(num1, num2);
System.out.println("결과 : "+result);
}//minus end
B는 A가 만들어줄 메소드의 이름과, 들어갈 수의 갯수 또한 인터페이스를 통해 알 수 있었기 때문에, 미리 실행할 파일을 만들어놓울 수 있었다.
그리고 A의 작업이 끝나면, 임의의 값을 넣어놓았던 test메소드를 A가 만든 메소드로 교체하면 되는 것 이다. 이렇게 공동 작업을 진행할 때, 가이드라인을 잡아놓고, 다시 코드를 짜는일이 없도록 해주는게 이 인터페이스라는 것이 하는 역할이다.
인터페이스를 사용하는 이유까지 정리해봤다. 오늘은 여기까지 하겠다! 안녕!
잘못된 정보가 나와있거나, 공유하고싶은 정보가 있으시다면 언제나 댓글 환영입니다!