추상화 (abstract, interface)
추상화란?
공통되는 성질을 추출하여 단순화하는 것 예) 지도 약도
추상 클래스
추상 메소드로 꼭 규격을 만들어야하는 클래스
추상 클래스는 오직 자식만이 객체화 시킬 수 있다. -> 자식에서 super()을 통해 생성자에 접근한다.
추상 클래스 및 추상 메소드를 선언하기 위해선 abstract 키워드를 사용한다.
실체 클래스는 추상 클래스를 상속한다.
package chap06.ex01.abs;
public class Main {
public static void main(String[] args) {
//Mammal - 추상클래스이기 때문에 자식 외에는 객체화시킬 수 없다.
//Mammal mal = new Mammal();
//Person
Person person = new Person("영희");
person.birth();
person.eat();
person.useTool();
person.social();
person.talk();
}
}
package chap06.ex01.abs;
//추상 클래스는 abstract 키워드가 붙어 있다.
public abstract class Mammal {
public String name;
public Mammal(String name) { // 추상클래스의 생성자는 오직 자식만 호출 가능하다.
this.name = name;
}
public void birth() {
System.out.println(name + "가 아이를 낳는다.");
}
public void eat() {
System.out.println(name + "가 젖을 먹인다.");
}
}
package chap06.ex01.abs;
public class Person extends Mammal {
public Person(String name) {
super(name);
}
//Person에 없는 name에 빨간줄이 안 뜨는 이유?
//Person은 Mammal을 상속받기 때문에 name을 본인 것처럼 사용한다.
public void useTool() {
System.out.println(name + "가 도구를 쓴다.");
}
public void social() {
System.out.println(name + "가 사회생활을 한다.");
}
public void talk() {
System.out.println(name + "가 대화를 한다.");
}
}
추상 클래스를 상속받으면 무조건 강제로 추상 메소드를 오버라이드 해야한다.
추상 클래스는 상속이기 때문에 1개만 상속 가능하다.
추상 메소드
추상 클래스와 인터페이스에서만 생성 가능하다.
실행문이 없다
자식이나 구현받는 클래스에서 오버라이드를 통해서만 사용 가능하다.
규격을 만들 때 사용된다. -> 규격을 통해 꼭 구현하고 사용해야 할 메소드를 만든다.
package chap06.ex02.absmethod;
public abstract class Abs {
public void parent() { //일반 메소드
System.out.println("그냥 쓰거나 오버라이드 할 수 있는 메소드");
}
//추상 메소드 - 추상클래스에서만 만들 수 있다.
//1. 몸체(실행문)이 없다.
//2. 오버라이드를 통해서만 사용 가능
public abstract void must1();
public abstract void must2();
}
package chap06.ex02.absmethod;
public class Concrete extends Abs {
//추상 메소드는 추상 클래스를 상속받으면 자동으로 오버라이드 된다.
//추상 메소드는 규격(필수로 필요한 기능들)을 만들 때 사용된다.
@Override
public void must1() {
}
@Override
public void must2() {
}
}
인터페이스
객체 사용 방법을 정의한다.
인터페이스 또한 추상 메소드를 통해 규격을 잡아주는 역할을 한다.
추상 클래스를 기본으로 삼기 때문에 public void~ 형태로 메소드를 정의하면 그게 바로 추상 메소드가 된다.
인터페이스는 객체화가 되지 않기 때문에 구현 받은 클래스를 객체화하여 사용해야 한다.
인터페이스에도 다형성이 적용된다.
jdk 1.8 이후로는 default 키워드를 통해 기본 메소드를 선언 가능하다. (default 메소드는 일반적인 형태로 사용하면 된다.)
jdk 1.8 이후로는 static 키워드를 통해 static 메소드를 선언 가능하다. (static은 원본에 접근해야 하기 때문에 인터페이스명.메소드명 형식으로 사용한다.)
인터페이스는 다중 구현, 다중 상속이 가능하다.
*인터페이스와 추상 클래스의 차이
interface | abstract | |
키워드 | interface | abstract |
추상메소드 | 사용 가능 | abstract 키워드 붙이면 사용 가능 |
일반메소드 | default 키워드로 사용가능 (jdk 1.8) | 사용 가능 |
정적메소드 | 사용가능 (jdk 1.8) | 사용 가능 |
사용 | 실체 클래스가 implements | 실체 클래스가 extends |
사용 개수 | 복수 개의 interface 구현 가능 | 1개의 추상클래스 상속 가능 |
객체화 | 불가능 | 자식 객체로 가능 |
package chap06.ex03.inter;
public class Main {
public static void main(String[] args) {
//인터페이스는 객체화가 되지 않기 때문에
//인터페이스를 구현받은 클래스를 객체화하여 사용한다.
//인터페이스에도 다형성이 적용된다.
MouseEvent evt = new Window();
evt.click(1);
evt.dblclick(2);
evt.wheel(1); //default 메소드는 그냥 사용하면 된다.
//static은 원본에 직접 접근해야 한다.
MouseEvent.setDpi(100);
}
}
package chap06.ex03.inter;
public interface MouseEvent {
//추상 클래스를 기본으로 삼고 있다.
public void click(int btn);
public void dblclick(int btn);
public double[] move();
//기본(default) 메소드(jdk 1.8부터 허용)
public default void wheel(int i) {
if(i==1) {
System.out.println("윗 방향 스크롤");
}else {
System.out.println("아랫 방향 스크롤");
}
}
//static 메소드(jdk 1.8부터 허용)
public static void setDpi(int dpi) {
System.out.println("감도 조절" + dpi);
}
}
package chap06.ex03.inter;
//인터페이스는 클래스에서 상속받는 것이 아닌 구현을 받는 것이다.
public class Window implements MouseEvent {
@Override
public void click(int btn) {
//클래스에서는 규격만 있는 인터페이스를 강제 오버라이드 하여
//실체를 만들어 준다. 그래서 구현(implements)라고 부른다.
System.out.println(btn + "번 버튼 클릭");
}
@Override
public void dblclick(int btn) {
System.out.println(btn + "번 버튼 더블클릭");
}
@Override
public double[] move() {
return null;
}
}
다중 구현
다중 구현이 된 클래스는 구현받은 인터페이스 형태에 모두 담길 수 있다. (다형성)
하지만 담겨진 인터페이스 형태에 따라 구현받은 메소드만 사용 가능하다. 예) keyEvent 형태에 담긴 Window 객체는 keyDown, keyUp만 사용 가능.
그래서 다중 구현으로 많은 인터페이스를 구현받는 방식은 비합리적이다.
package chap06.ex04.impl;
public interface KeyEvent {
public void keyDown(int key);
public void keyUp(int key);
}
package chap06.ex04.impl;
import chap06.ex03.inter.MouseEvent;
public class Window implements MouseEvent, KeyEvent {
//KeyEvent
@Override
public void keyDown(int key) {
}
@Override
public void keyUp(int key) {
}
//MouseEvent
@Override
public void click(int btn) {
}
@Override
public void dblclick(int btn) {
}
@Override
public double[] move() {
return null;
}
}
package chap06.ex05.ins;
import chap06.ex03.inter.MouseEvent;
import chap06.ex04.impl.KeyEvent;
import chap06.ex04.impl.Window;
public class Main {
public static void main(String[] args) {
//다중 구현이 된 클래스는 두 가지 형태에 모두 담길 수 있다.
//다만 담겨진 형태에게 구현받은 메소드만 사용 가능하다.
KeyEvent key = new Window();
key.keyDown(23);
key.keyUp(23);
MouseEvent mouse = new Window();
mouse.click(1);
mouse.dblclick(2);
mouse.move();
//이런 식으로 사용하는 것은 비합리적이다.
}
}
익명 객체
인터페이스는 익명 객체를 사용할 수 있다.
원래 인터페이스는 구현해줄 실체가 없으므로 구현해줄 클래스가 필요하다.
익명 객체를 이용하면 구현 클래스가 필요 없다.
익명 객체란 객체화할 클래스가 존재하지 않아 즉석으로 클래스를 생성하므로 이름이 없다.
package chap06.ex05.ins;
public interface Common {
public void test1();
public void test2();
}
package chap06.ex05.ins;
public class CommonImpl implements Common {
@Override
public void test1() {
System.out.println("test1");
}
@Override
public void test2() {
System.out.println("test2");
}
}
package chap06.ex06.anony;
import chap06.ex05.ins.Common;
import chap06.ex05.ins.CommonImpl;
public class Main {
public static void main(String[] args) {
//일반적인 객체화
Common comm = new CommonImpl();
//익명 객체 - 인터페이스를 객체화하려고 하면 만들어진다.
//인터페이스는 구현해줄 실체가 없으므로 구현해줄 클래스를 필요로 한다.
//익명객체를 이용하면 구현 클래스가 필요 없다.
Common anony = new Common() {
@Override
public void test2() {
System.out.println("익명객체 test2");
}
@Override
public void test1() {
System.out.println("익명객체 test1");
}
};
comm.test1();
comm.test2();
anony.test1();
anony.test2();
//CommonImpl의 경우는 다른 클래스에서 불러서 사용할 수 있다.
//anony는 익명객체이므로 여기(해당 클래스)에서만 사용할 수 있다.
//익명이라는데, 어떤 이름이 없는 것인가?
//객체화할 클래스가 존재하지 않는다. -> 즉석으로 클래스를 생성 -> 그래서 이름이 없다.
}
}
인터페이스 상속
인터페이스도 상속이 가능한데 클래스와의 차이점은 다중 상속이 가능한 점이다.
클래스는 기능을 확장해 나가는 위계 구조이고 인터페이스는 규격을 정하는 모듈 구조이기 때문이다.
다중 상속을 받음으로써 다중 구현에서의 문제점 (다중 구현받았을 때 한 인터페이스 형태로 들어가면 다른 인터페이스의 메소드를 사용 못하는 점)을 해결할 수 있다.
package chap06.ex07.ext;
import chap06.ex03.inter.MouseEvent;
import chap06.ex04.impl.KeyEvent;
public interface Child extends KeyEvent, MouseEvent {
public void textField(String content);
public void selectOne();
public void checkOne();
}
package chap06.ex07.ext;
//Child만 구현 받았지만
//Child가 상속한 KeyEvent와 MouseEvent 내용도 구현된다.
//규격을 만들기 위해서 필요한 여러 규격을 한 번에 추가할 수 있다.
//인터페이스가 다중 상속을 허용하기 때문에 가능한 일이다.
public class ChildImpl implements Child {
@Override
public void keyDown(int key) {
System.out.println(key+"번 키 누름");
}
@Override
public void keyUp(int key) {
System.out.println(key+"번 키 뗌");
}
@Override
public void click(int btn) {
System.out.println(btn+"번 버튼 클릭");
}
@Override
public void dblclick(int btn) {
System.out.println(btn+"번 버튼 더블 클릭");
}
@Override
public double[] move() {
return null;
}
@Override
public void textField(String content) {
System.out.println(content);
}
@Override
public void selectOne() {
System.out.println("아이템 선택");
}
@Override
public void checkOne() {
System.out.println("아이템 체크");
}
}
package chap06.ex07.ext;
public class Main {
public static void main(String[] args) {
//Child는 KeyEvent, MouseEvent, Child 모두 수용 가능
//상속을 받으면 다중 구현 시에 문제가 되었던 부분이 해결 (A,B,C 구현받고 A의 형태로 들어가면 B,C를 사용 못한다)
Child ch = new ChildImpl();
ch.click(2);
ch.dblclick(1);
ch.keyDown(23);
ch.keyUp(23);
ch.selectOne();
ch.checkOne();
ch.textField("아무거나 입력");
}
}
인터페이스와 추상 클래스 사용하는 이유?
전체적인 규격을 만들어줘서 결합도를 낮출 수 있고 코드의 중복을 줄여준다.
-> 규격이 있다면 다른 것과 충돌없이 사용할 수 있기 때문이다.
결합도란?
외부 모듈과의 연관도 또는 모듈 간의 상호의존도이다.
결합도 줄인 예시) 세 가지 종류의 빙수를 만드는데 공통으로 들어가는 작업들은 인터페이스를 구현받아 일반 메소드로 사용하고 각각의 빙수에서 넣어야 하는 재료들은 각자의 클래스에서 규격(추상메소드)를 오버라이드하여 사용해보자.
package chap06.ex08.couple;
public interface Vingsu {
// 기본적인 기능
public default void iceFlake() {
System.out.println("얼음을 갈아 넣는다.");
}
public default void milk() {
System.out.println("우유를 넣는다.");
//System.out.println("스팀 밀크를 넣는다."); //여기서만 바꾸면 구현하여 쓰는 모든 클래스에 적용된다.
}
public default void redBean() {
System.out.println("팥을 넣는다.");
}
public default void jelly() {
System.out.println("젤리를 넣는다.");
}
public void etc(); // 무조건 넣어야 하며, 각자 알아서 사용한다.
}
package chap06.ex08.couple;
public class ChocoVingsu implements Vingsu {
@Override
public void etc() {
System.out.println("초콜렛을 갈아 넣는다.");
}
}
package chap06.ex08.couple;
public class CookieVingsu implements Vingsu {
@Override
public void etc() {
System.out.println("쿠키를 갈아 넣는다.");
}
}
package chap06.ex08.couple;
public class RedBeanVingsu implements Vingsu {
@Override
public void etc() {
System.out.println("추가 첨가 없음");
}
}
package chap06.ex08.couple;
public class VingsuConcrete {
//매개변수 다형성
public void makeVingsu(Vingsu ving) { //어떤 객체가 오든지 Vingsu를 구현 받았으면 받아 준다.
ving.iceFlake();
ving.milk();
ving.redBean();
ving.jelly();
ving.etc();
}
}
package chap06.ex08.couple;
public class Client {
public static void main(String[] args) {
VingsuConcrete ving = new VingsuConcrete();
// makeVingsu를 통해서 원하는 빙수를 만든다.
ving.makeVingsu(new ChocoVingsu()); //초코빙수
System.out.println();
ving.makeVingsu(new RedBeanVingsu()); //팥빙수
System.out.println();
ving.makeVingsu(new CookieVingsu()); //쿠키빙수
}
}