저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 추상 클래스가 좀 더 '추상화'된 인터페이스에 대해서 살펴보겠습니다.
추상 클래스를 잘 모르시겠다면 다시 한번 보고 오시면 더욱 도움이 될 것입니다.
http://sims-solve.tistory.com/10?category=747624

추상 클래스 편에서도 '추상'이라는 단어를 살펴보았습니다. 위에서 언급했지만, 인터페이스(interface)는 추상 클래스보다 한 단계 더 '추상화' 된 것입니다.
잠깐 추상 클래스에 대해서 떠올려봅시다. 추상 클래스는 하나 이상의 추상메소드로 구성되어있으며, 상속받는 클래스에서 반드시 추상메소드를 구상메소드로 구현해야 했습니다.(구상메소드 => 구현된 메소드) 그럼! 추상 클래스보다 더 추상화된 인터페이스는 어떨까요? 
'추상화' 한다는 건 추상메소드처럼 공통되는 특성, 속성을 추출하여 선언만 하고 다른 곳에서 알맞게 구현하는 것이었습니다.
인터페이스는 추상 클래스에서 더 나아가 모든 메소드를 abstract 한 것입니다. 변수는 상수만 정의할 수 있습니다.
예시를 보면서 이야기해 봅시다.

 (1)public interface shapeInterface {
	public int k = 3;
    public void area();
}
=========================================
public class rectangle implements shapeInterface {
	public static void main(String args[]) {
		rectangle test = new rectangle();
		
		//test.k=5;  ------> shapeInterface의 k를 변경하려고 할 때 error
		System.out.println(shapeInterface.k); // 3
		System.out.println(k); // 3 
		
	}
	@Override
	public void area() {
		System.out.println("넓이구하기");
	}
}

(1)처럼 간단하게 예시를 보도록 하겠습니다.
일단 Class를 만드는 것처럼 만들되, class가 아닌 interface 키워드를 사용하여 Interface로 만들게 됩니다. 이렇게 인터페이스(interface)로 만들게 되면 자동으로 결정되는 것이 있습니다. 변수는 static final, 메소드는 abstract 키워드를 써주지 않아도 변수는 static final, 메소드는 abstract 키워드를 사용 한 것처럼 됩니다. 증명해봅시다.
rectangle에서 객체 test를 만들어 test.k를 통하여 interface의 k 값을 5로 변경하려고 하면
"The final field shapeInterface.k cannot be assigned"라는 에러를 볼 수 있습니다. final 필드를 바꿔줄 수 없다는 소리죠. interface에서 변수를 선언하면 자동으로 상수(final)이 된다는 소리죠.
한 가지 더 있습니다."System.out.println(shapeInterface.k);" 이 부분입니다. 이 부분을 보시고 한 가지 떠올라야 하는 키워드가 있습니다. 바로 static이죠. 하지만 우리는 interface에 static을 지정하지 않았죠. 여기서 알 수 있듯, interface에서 선언한 변수는 반드시 static이 된다는 것 또한 알 수 있습니다.
정리해보자면, 따로 명시해주지 않아도 자동으로 final static 변수가 되는 것이죠.

이번엔 메소드 입니다. 메소드는 추상메소드와 같습니다. interface 안에서도 구상메소드(구체적으로 구현된 메소드)를 선언하지 못합니다. 하지만 추상메소드처럼 abstract 키워드를 명시적으로 사용하지 않아도 자동으로 abstract 됩니다. 즉. "public void area();"만 하더라도 자동으로 abstract 된다는 것이죠.
이제 상속받는 클래스에선 반드시 interface의 메소드들을 구상메소드로 구현해 줘야 하죠.
내용은 추상메소드와 같습니다. 그럼 interface를 사용하는 이유는 뭘까요? 무슨 이점이 있는 걸까요?
객체지향 프로그래밍은 클래스 간에 결합도가 낮을수록 좋습니다. 이유는 A 클래스가 바뀌면 B 클래스까지 수정해야 한다면, 개발자 입장에선 좋을까요? 싫습니다. 이러한 결합도를 낮추기 위해 '추상화'를추구하는 것입니다.

자 그럼 추상 클래스 vs 인터페이스를 정리하고 마무리해보겠습니다.
추상 클래스 =  추상메소드(1개 이상) + 구상메소드 / 인터페이스 = 추상메소드(only)
추상 클래스 변수 = 일반 변수 / 인터페이스 변수 = static final 변수
추상 클래스 상속 = extends / 인터페이스 상속 = implements 
클래스 상속 개수 =  1개(추상 클래스 or 클래스) / 인터페이스 상속 개수 = 무한대

이렇게 간단하게 정리해 보았습니다. '추상'이라는 말이 계속 나오고 있습니다. 한번 다시 곰곰이 생각해보시고 정리하시길 권합니다. 오늘은 여기까지입니다!

다음 편은 내재 클래스(Nested class)를 살펴보겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 클래스 중 추상 클래스(abstract)를 살펴보죠.
'추상'이라는 단어의 뜻을 알고 계시나요?
국어사전에는 '여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용'
이라고 적혀있습니다.
자바에서도 추상 클래스(abstract)는 국어사전의 의미와 동일합니다.
자바에서는 공통이 되는 메소드를 파악해 추상메소드를 만드는 것이 추상 클래스에서 가장 중요합니다.
간단한 예를 들어보죠. 넓이를 구한다고 생각해봅시다. 그럼 네모의 넓이는 어떻게 구하나요? x, y 값을 곱하여 구합니다. 그럼 세모는요? x * y / 2를 하면 됩니다. 누구나 알고 있는 것이죠.
하지만 이걸 이제 클래스로 표현해야 한다면 어떻게 하실 건가요? 혹시 세모 클래스, 네모 클래스를 각각 만들어 사용하실 건가요? 그러기엔 x, y좌표가 있어야 하고 넓이를 구하는 메소드가 있다는 공통점이 눈에 띄지 않나요? 

위 국어사전의 '정의처럼 공통되는 특성이나 속성 따위를 추출'하여 새로운 클래스를 구성해 보면 어떨까요? 소스코드를 보시죠.

(1)public class shape {
	  int x;
	  int y;
	  public void area() { //??????
	  }
}
==================================
public class rectangle extends shape { }
==================================
public class triangle extends shape{ }

(1) 소스코드와 같이 shape란 클래스를 만들어서 네모, 세모에 공통이 되는 부분을 모두 넣은 클래스를 만들었습니다. 이와 같이 shape란 클래스를 만들어서 상속 형태로 사용하는 것이 이상적이라 볼 수 있습니다. 하지만 문제가 있습니다. 네모, 세모의 넓이를 구하는 메소드(area)는 각자 넓이를 구하는 공식이 다르다는 것입니다. 하지만 '넓이'를 구해야 한다는 속성은 변함이 없어 shape에 공통된 메소드로 들어가게 되는 것이죠. 이 문제를 어떻게 해결할 수 있을까요? '오버라이딩'이 단숨에 생각이 나셔야 합니다.
오버라이딩을 사용하여 rectangle, triangle 각각 클래스에서 재지정하여 넓이를 구해야 하죠.
하지만 우리가 배운 '오버라이딩'은 부모 클래스에 메소드가 온전히 정의가 되어있을 때 사용했었습니다. 여기서 생각해 봐야 하는 부분은 shape에 넓이를 구하는 메소드가 완전히'정의'될 필요가 있는가입니다. 이 뜻은 shape 안에 area는 반드시 자손 클래스에 가서 서로 다른 형태로 사용된다고 우리는 이미 알고 있습니다. 그럼 shape 안에서 완전하게 정의가 될 필요는 없는 거죠. (완전히 정의된다는 것은 메소드가 수행하는 부분 { }이 들어가야 한다는 뜻)
그래서 사용하는 게 abstract 키워드입니다. abstract 키워드는 간단하게 말해서 상속받는 부분에서 '반드시' abstract로 된 메소드를 반드시 정의해주겠다는 소리입니다!
예제를 보시죠!

(2)public abstract class shape { //하나라도 추상메소드가 있으면 추상클래스가 되야함!
	int x=2;
	int y=2;
	
	public abstract void area(); // abstract 메소드! 
}
======================================
public class rectangle extends shape {
	@Override //오버라이드!
	public void area() {
		System.out.println(this.x*this.y);
	}
}
=======================================
public class triangle extends shape{
	@Override
	public void area() {
		System.out.println(this.x*this.y/2);
	} 
	public static void main(String args[]) {
		new rectangle().area();
		new triangle().area();
	}
}

(2)와 같이 (1)의 코드를 고쳐보았습니다. 가장 눈에 띄는 건 shape의 area를 abstract 키워드를 사용하여 추상메소드로 선언했다는 것입니다. 이렇게 되면, shape 자체 area메소드는 실질적으로 구하는 공식이 없어 훨씬 추상적이게 되는 것이죠.(공통적이 부분을 잘 뽑아내게 되는 것이죠)
하지만 반드시 추상메소드를 선언하게 되면 추상메소드가 속한 클래스는 반드시 추상 클래스가 돼야 합니다. 추상메소드가 하나라도 있게 된다면 추상 클래스가 되는 것이죠. 추상 클래스가 된다고 해서 클래스와 크게 바뀌는 것은 없습니다. 단, 추상 클래스를 상속받는 클래스는 추상메소드를 '반드시' 클래스 안에서 재정의 해야 합니다.(오버라이딩과 같이 말이죠)

하지만 의문점이 하나 생길 수 있습니다. 그럼 rectangle, triangle 따로따로 area 메소드를 사용하면 되는 거 아니냐?라는 점입니다. 물론 맞는 말입니다. 상관없습니다. 하지만.. 지금처럼 추상화를 잘 해놓게 된다면 이점이 있습니다. '형식'을 가지게 되는 것입니다. 살면서 한 번씩은 보고서, 자기소개서, 리포트 등등.. 을 써보셨을 겁니다. 혹시 백지인 A4용지에 구구절절 써 내려가는 것과 '형식'이 적혀있는 A4 종이에 맞춰 적어 내려가는 것 중 어느 것을 선택할 건가요? 대개 '형식'이 있는 것을 선택하지 않을까요? 
이 '형식'을 통하여 작성자는 어떤 것을 써 내려가야 하는지 방향성을 알 수 있지 않을까요?
자바에서도 마찬가지라고 생각합니다. '형식'을 만들어 방향성을 제시하고 추후에도 재사용 하겠다는 의미입니다. 추상화 클래스를 잘 구성하게 된다면, 보다 짜임세있는 '형식'을 가지는 코드를 작성할 수 있지 않을까요? 오늘은 여기까지입니다!

오늘은 추상클래스(메소드)편을 살펴보았습니다. 어려운 것은 없었습니다. 다시 한번 잘 생각해보시고 '추상화'가 줄 수 있는 이점에 대해서 살펴보시는 것도 좋을 것 같습니다. 

다음 편은 Interface(인터페이스)를 살펴보고 추상 클래스와 비교까지 해보도록 하겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 자바 프로그램에 있어서 유용하게 사용할 수 있는 키워드를 몇 가지 살펴보고자 합니다.
상속, 객체를 배우셨으면 당연히 알아야 하는 this 와 super를 알아보겠습니다.

첫 번째로 this - 이것을 뜻하는 영어인데요.
자바에서 '이것' this는 < 메소드를 부른 객체 > 정도로 생각하시면 됩니다.
메소드를 수행할 때, 우리는 객체를 생성하여 객체. 메소드로 메소드를 수행하게 되죠.
this는 메소드 안에서만 사용할 수 있습니다. 이유는 간단합니다. 위에서 말했듯 A.메소드 형태로 메소드를 부르면, A 객체 안에 있는 메소드를 수행하는 거죠. 이렇게 A.메소드 형태는 현재 어떤 객체를 이용하고 있는지 알 수 있는 징표가 됩니다. 이렇기 때문에 this는 A를 나타낼 수 있는 것이죠.
만약 메소드가 아닌 곳에서 선언하게 된다면, 객체명.~~~로 접근을 하는 것이 아니기에 this는 어떤 객체를 가리키는지 알 수가 없죠. 결국 this는 사용 못 하게 되는 것이죠. (변수는 객체명.변수 로 접근하지만 이것이 끝이기에 this를 사용하여 할 수 있는 것이 없습니다.)
이와 같은 이유로 static 메소드도 동일합니다. static메소드는 객체와 상관없이 하나를 공유하는 메소드라고 잠깐 설명드렸는데요.
static메소드는 객체를 생성하지 않아도 클래스명.static메소드명으로 호출이 가능합니다. 이와 같이 객체가 없어도 호출이 가능하기에 this의 사용을 허용하지 않습니다.

다음은 super입니다. 현재 객체를 나타내는 this가 있다면, super는 상위 클래스 객체를 나타내는 겁니다. super는 상속받은 자손 객체에서 사용할 수 있습니다. 예를 보시죠.


(1)public class parent {
	public void say() {
		System.out.println("부모");
	}
}
(2)public class son extends parent{
	public void say() {
		super.say();
		System.out.println("자손");
	}
	public static void main(String[] args) {
		son myson = new son(); // son 객체생성
		myson.say(); // 최종 출력 : 부모 자손
	}
}

parent를 상속받은 son 클래스가 있습니다. 저번 시간에 형 변환을 해서 parent에 있는 메소드를 수행하고자 했으나 실패를 했죠. 하지만 parent의 메소드를 수행하는 방법이 있다고 말씀드렸었습니다.
바로 super 키워드죠. (2) say() 메서드 안에 super.say()라고 되어있습니다. super 키워드는 자식-부모 관계(상속)에서 부모 객체라 생각하시면 되고, 부모객체.메소드를 사용하여 부모의 메소드를 수행할 수 있습니다. 조금 더 자세히 말하자면, 상속을 받은 son 클래스를 객체를 만들게 되면, son, parent 객체가 만들어져. myson이라는 객체에 들어 있다고 생각하시면 됩니다. 이 때문에 다형성, 형 변환이 가능한 것이기도 하고요. 어쨌든 myson은 son, parent 객체를 가지고 있기에 super라는 키워드를 사용하면, 현재 객체(son)의 부모(parent) 객체에 접근하여 부모의 메소드를 사용할 수 있는 것이죠.

마지막으로 final입니다.
final은 변수를 지정할 때 변하지 않는 '상수'로 많이 사용합니다.( 물론 다른 곳에서도 사용합니다.)
final 키워드를 가지게 되면, 바꿀 수 없습니다. final 변수명 = ~~~;로 선언하게 된다면, 선언한 곳에서만 내용을 바꿀 수 있습니다.


(3)public class Solution {
	
	public final int k = 3; //선언 한 곳 
	
	public static void main(String[] args) {
		Solution a = new Solution();
		a.k=6; // X 오류발생
	}
}

(3) 코드는 fianl로 int를 선언해보았습니다. 객체 a를 만들어 a.k로 접근하여 값을 변경하려니 오류가 발생합니다. 이처럼 final은 선언한 곳 에서만 값을 바꿀 수 있지 다른 곳에선 바꿀 수 없는 '상수'가 됩니다. 그럼 final 메소드를 알아보죠.


(4)public class parent {
	  public final void say() {
	  	System.out.println("부모");
	}
}
---------------------------------------------------------------------
public class son extends parent{

	public void say() {   // X 오류
		System.out.println("자손");
	}
	public static void main(String[] args) {
		son myson = new son(); 
		myson.say(); 
	}
}

(4) 코드에서 보듯이 상속 관계에서 parent의 say를 final로 지정하였습니다. 다시 말씀드리지만 final은 변할 수 없는 상태가 되는 것과 같습니다. 그래서, 자손 son에서 say를 오버라이딩을 시도하니 오류가 나오는 것을 알 수 있습니다.(오버라이딩은 메소드를 재정의하는 것이기 때문에)

다음은 final 클래스입니다.


(5)public final class parent { }
-----------------------------
   public class son extends parent{ }  //오류 

(5) 번 코드는 fianl로 선언된 parent를 son에 상속해주려고 했더니 오류가 발생합니다. 이유는 '상속을 위해 디자인된 클래스를 제외한 클래스를 상속하여 프로그램의 디자인을 깨고, 예상치 못한 일이 발생하는 것을 막아주기 위한 수단'이라는 설명이 있습니다. 개인적으로 맞다고는 공감하지만 말이 어렵죠.
저는 이렇게 생각합니다.'오버라이딩이 될 수도 있으니까.' final 클래스는 메소드를 선언하면 final을 붙이지 않아도 final메소드가 됩니다. 위에서 보셨듯 final메소드는 자손에서 오버라이딩 할 수 없습니다.
이러한 '위험요소' 가 있기에 처음부터 제한했다고 생각합니다.
구구절절 길어졌지만, final을 사용하면 원치 않은 상속을 피할 수 있습니다.
또 final로 선언된 메소드는 실행 속도가 빠르다고 합니다. 

오늘은 여기까지입니다! 자바 프로그램에서 없어도 되지만 있으면 훨씬 도움이 되는 3가지 키워드를 살펴보았습니다. 단순하게 이 3가지 키워드가 어떤 역할을 하는지 기억하셔야겠지만, 왜 사용 가능한지도 한번 곰곰이 생각해 보시면 더욱더 프로그래밍 하는데 도움이 되지 않을까 생각합니다.

다음 편은 추상 클래스 편을 써보겠습니다.

+ Recent posts