지금부터는 클래스의 종류에 대해서 알아볼 것이다.
추상적인 내용만 정의하고 있는 클래스로 구체적인 내용은 하위 클래스에서 구현되도록 해놓은 클래스
추상클래스는(부모) 클래스들의(자식) 공통요소들을 가상화 시켜놓는다.
객체화는 가능하나 잘 사용하지 않는다. (목적에 어긋난다.)
부모 추상클래스의 역할은 단지 자식클래스를 만들 때 도와주는 역할을 수행하기 위해 존재한다.(다형성)
자식클래스에서 재정의할 메서드의 이름만 가지고 있다.
즉, {}를 가지지 않는다.
그 메서드를 추상 메서드라고 하며, abstract 키워드가 앞에 붙는다.
하지만 일반 메서드도 선언 가능하다.
강제성을 부여하는 이유는 ?
전자제품은 키고 끄는 기능이 꼭 있어야 한다. 깜빡해서는 안된다.
그래서 키고 끄는 것을 강제적으로 만들게끔 하는 것이다.
자식 클래스에서 사용할 메서드의 이름만 갖고 있다. (미구현 상태) 즉, {}를 갖지 않는다. ({}를 바디라고 함)
abstract 추상클래스명 {
abstract 추상메서드명();
}
그동안 우리가 클래스 명을 정할 때 개념 자체가 추상적인 개념이었다.
그것을 실체화 시키는 것을 객체라고 하였다.
추상클래스는 상속의 개념을 가지고 있다.
도형이라는 클래스 안에 '그리다' 라는 기능 구현을 할 수 있을까? 없다. 도형은 추상적인 개념이기 떄문에.
그럼 '그리다' 라는 기능을 어디에서 구현하면 될까?
삼각형, 원, 사각형. 지금까지는 이 녀석들을 객체로 만들어 객체로 접근하는 것을 배웠었다. 하지만 이 친구들은 다 그리는 방식이 다르다.
아무리 내가 도형에서 그리기를 구현했어도 뭘 그릴지를 모르는 것이다.
그래서 우린 매개변수로 받아와서 삼각형 생성자에다 무엇을 그릴지를 넣어주고 하는 방식을 썼었다.
이제는 삼각형, 원, 사각형 이 세가지를 객체가 아닌 클래스로 만들기로 하자.
자식클래스에서 추상클래스를 상속받은 후 추상클래스에서 선언만 했던 메소드를 자식클래스에 맞게끔 재정의하자.
추상클래스는 선언만 해줄 뿐 상속하는 클래스(자식)에게 재정의의 강제성을 부여한다.
package abstract01;
public abstract class Shape {
double area;
abstract void showArea(double w, double h);
// 자식클래스는 부모클래스의 메소드를 재정의할 수 있었지만,
// final을 이용해 자식클래스에서 부모클래스 메소드의 재정의를 막기 위해 사용
final void function() {
System.out.println("추상 클래스의 일반 메서드입니다.");
}
}
class Rect extends Shape {
// 추상클래스 Shape를 상속한다면, 오버라이드하여야 한다.
@Override
void showArea(double w, double h) {
area = w*h;
System.out.println("넓이 : " + area + "cm²");
}
}
class Tri extends Shape {
@Override
void showArea(double w, double h) {
area = (w*h) / 2;
System.out.println("넓이 : " + area + "cm²");
}
}
출력해보자.
package abstract01;
public class Board {
public static void main(String[] args) {
new Rect().showArea(10, 50);
new Tri().showArea(30, 15.9);
}
}
Rect와 Tri 모두 Shape의 function 재정의는 불가능하다. (final)
추상클래스를 고도화 시킨 문법이며, 반드시 추상메서드와 상수만 선언해야 한다. 다른 클래스에서 인터페이스를 지정할 때에는 implements를 사용한다. 지정된 인터페이스가 가지고 있는 추상메서드들을 반드시 재정의 해야한다.
바로 한번 인터페이스를 만들어보자.
package Interface;
public interface Solidier {
int arms = 2; // final static 이 생략되어있음
final static int legs = 2;
abstract void eat();
void work();
void play();
void sleep();
void salute();
}
인터페이스를 구현하는 클래스들을 만들어보자
이등병. 인터페이스의 모든 메서드를 구현한다.
package Interface;
public class Private implements Solidier{
@Override
public void eat() {
System.out.println("천천히 각을 재서 90도로 앉아서 먹는다.");
}
@Override
public void work() {
System.out.println("아주 열심히 일하지만 결과물이 없다.");
}
@Override
public void play() {
System.out.println("안타깝게도 남은 일들이 많아서 꿈에서 논다.");
}
@Override
public void sleep() {
play();
System.out.println("잠에 들지 못한다.");
}
@Override
public void salute() {
System.out.println("충!!성!!");
}
}
일병도 마찬가지
package Interface;
public class Pfc implements Solidier {
@Override
public void eat() {
System.out.println("조금 식사를 빨리 하고 후임들을 챙겨야 한다.");
}
@Override
public void work() {
System.out.println("점점 일을 마치고 결과물이 생기고, 후임들 일도 도맡아 한다.");
}
@Override
public void play() {
System.out.println("개인 정비 시간을 잘 활용하여 사지방과 노래방을 갈 수 있다.");
}
@Override
public void sleep() {
System.out.println("잠에 잘 들지만 아침 6시 30분에 눈이 떠진다.");
}
@Override
public void salute() {
System.out.println("충!성!");
}
}
상병도 마찬가지
package Interface;
public class Corporal implements Solidier{
@Override
public void eat() {
System.out.println("밖의 경치를 보며 여유롭게 식사한다.");
}
@Override
public void work() {
System.out.println("업무 지시를 한다.");
}
@Override
public void play() {
System.out.println("달려간다");
}
@Override
public void sleep() {
System.out.println("세상 모르게 잔다.");
}
@Override
public void salute() {
System.out.println("충ㅅ");
}
}
병장은 일을 하지 않아도 된다면? 즉 메서드를 필요한 것만 재정의하고 싶다면?
Army라는 어댑터를 생성한다. 내용을 살펴보자.
package Interface;
// Adapter : 이미 인터페이스를 추상클래스에 지정해놓은 어댑터
public abstract class Army implements Solidier {
// 솔져에 있는 강제성이 부여된 아이들을 골라서 쓰자는 목적.
// 솔져는 써야 하는데 그 안에서 분명 재정의 안해도 될 것들이 있다.
// 그럴 때는 implements가 아닌 extends를 하면 된다.
@Override
public void eat() {
// TODO Auto-generated method stub
}
@Override
public void work() {
// TODO Auto-generated method stub
}
@Override
public void play() {
// TODO Auto-generated method stub
}
@Override
public void sleep() {
// TODO Auto-generated method stub
}
@Override
public void salute() {
// TODO Auto-generated method stub
}
}
병장 Sergeant class 를 확인해보자.
Army라는 어댑터를 상속하면서 내가 재정의하고 싶은 것만 불러들였다. 오류가 나지 않는다.
package Interface;
public class Sergeant extends Army {
@Override
public void eat() {
System.out.println("밥 먹고 PX ");
}
@Override
public void sleep() {
System.out.println("하루 종일 잔다.");
}
@Override
public void play() {
System.out.println("하루 종일 논다.");
}
@Override
public void salute() {
System.out.println("ㅊ");
}
}
클래스들의 공통 그룹명으로 인터페이스를 생성하고 아무것도 구현하지 않는다. 그럼 이걸 왜 만들어놓을까? 그 클래스들이 같은 그룹으로 묶였다는 표시를 하기 위해 사용한다.
package marker;
public interface Animation {
}
package marker;
public class Digimon implements Animation {
}
package marker;
public class Pokemon implements Animation {
}
package marker;
public class ZzanGu implements Animation {
}
package marker;
public class Harrypotter {
}
체크해보자 !
package marker;
public class Check {
public static void main(String[] args) {
Digimon d = new Digimon();
Pokemon p = new Pokemon();
ZzanGu z = new ZzanGu();
Harrypotter h = new Harrypotter();
if(d instanceof Animation) {
System.out.println("디지몬은 애니메이션입니다.");
}
if(p instanceof Animation) {
System.out.println("포켓몬은 애니메이션입니다.");
}
if(z instanceof Animation) {
System.out.println("짱구는 애니메이션입니다.");
}
if(!(h instanceof Animation)) {
System.out.println("해리포터는 애니메이션이 아닙니다.");
}
// 어느 그룹 안에 속해있는지 검사해주는 마커인터페이스 역할 Animation
}
}