추상화
추상화는 OOP의 4대 특성 중 하나로,
실체 간에 공통되는 특성을 추출하고 불필요한 정보를 제거해 단순화 시키는 것이다.
OOP의 4대 특징을 다시 살펴보면
Encapsulation(캡슐화) : 다른 사용자에게 데이터 입/출력을 제한한다.
Inheritance(상속) : 나에게 없는 기능을 부모에게 받아서 내 것 처럼 쓴다.
Polymorphism(다형성) : 부모의 형태에 자식이 들어갈 수 있다.
Abstrack(추상화) : 불필요한 정보를 제거해 단순화시킨다.
추상화가 잘 된 사례 중 하나는 약도이다.
굉장히 복잡한 지도 중 중요한 건물, 지하철 역 등 필요한 부분만 남기는 것이 약도인 것처럼 추상화 또한 비슷하다.
두 번째는 캐리커쳐 이다.
캐리커쳐도 사람의 복잡한 얼굴에서 필요한 특징만을 남겨 그림을 그려내는 것처럼 추상화도 필요한 부분만 남기고 불필요한 부분은 날려버리는 것이다.
❔ 그럼 추상화는 왜 사용하는 걸까?
인터넷에 검색해보면 추상화를 사용하면 "결합도를 느슨하게 해준다"라는 말을 쉽게 볼 수 있을 것이다.
결합도가 느슨하다는 것은 결합도가 낮다라는 표현과 동일하다고 생각할 수 있고, 결합도가 높다는 것은 1개를 수정하기 위해 많은 부분을 건드려야 하는 경우를 말한다.
프로그래밍은 응집도는 높을수록, 결합도는 낮을수록 좋다.
추상클래스 (Abstrack class)
추상 클래스는 공통으로 사용할 기능, 구현해야 할 기능 즉 규격을 잡을 때 사용한다.
추상 클래스로 추상 메소드를 만들어 해당 메소드는 꼭 구현(override)해야 한다는 규격을 만들 수 있다는 것이다.
이러한 추상 클래스는 여러 특징이 있다.
abstract 키워드를 붙여야 한다.super()를 호출해야 한다.실체 클래스(concrete class)는 추상클래스(abstrack class)를 상속한다.override할 수 있다.그렇다면 추상 클래스는 추상 메소드 (공통으로 사용할 기능과 구현 해야 할 기능)의 모음으로 구성되어 있는 것이라면 추상 메소드란 무엇일까?
추상 메소드
추상 메소드는 형태만 있고, 몸체는 없는 메소드를 말한다.
이러한 추상 메소드는 특정한 메소드를 강제로 override 해야 할 경우에 설정을 해준다.
추상 메소드는 코드블록 즉 몸체가 없다.
이는 이름만 존재하고, 행위가 존재하지 않다는 것이다.
선언 방법은 abstract 를 붙여 만들어주면 된다.
public abstrack void must();
이렇게 추상메소드를 넣은 추상클래스를 상속받게 되면 상속과 동시에 강제로 override가 된다.
그렇다면 이게 왜 규격이란 말인가?
규격은 무언가를 정해주는 것이다.
차를 만들 때 바퀴는 꼭 있어야 하고 크기는 몇으로 해야해라고 규격을 지정해주는 것처럼 프로그램에서는 기본적으로 꼭 해야하는 것들을 추상메소드로 만들어두고 이 클래스를 상속 받으면 이건 꼭 만들어줘야 한다는 규격을 정해주는 것이다.
또한, 오버라이드를 하면 몸체를 작성할 수 있으니 자식만 사용하라는 의미도 있다.
✔ 정리
1. 추상화는 공통되는 특징을 추출하여 단순화 시키는 작업이다.
2. 추상 클래스, 추상 메소드는 abstract 키워드를 사용한다.
3. 추상 클래스는 자식 이외에는 객체화 시킬 수 없다.
4. 추상 클래스를 상속 받으면 추상 메서드를 강제로 override 하게 된다.
5. 이 과정을 통해 꼭 필요한 메소드를 만들어 규격화 시킬 수 있다. (이건 꼭 해야해 라고 말하는 것)
인터페이스(Interface)
인터페이스는 규격을 잡기 위해 만드는 것으로 기본이 추상 메소드이다.
인터페이스는 실제로 하면 복잡한 것을 간단하게 할 수 있도록 도와주는 도구라고 앞에서 설명했었다.
예를들어 브레이크와 스위치를 말할 수 있다.
브레이크와 스위치를 생각해보면 브레이크는 밟는다는 규격이 있고, 스위치는 누른다는 규격이 있다.
외에도 스마트폰을 터치하면 앱을 실행시킬 수 있고, 길게 누르면 앱을 움직일 수 있듯이 우리가 사용하는 기기들은 모두 규격이 있다.
그래서 인터페이스는 간단하게 해주는 것에 규격이 꼭 함께 붙어있기 때문에 인터페이스의 주목적도 규격화이다.
그렇다면 추상클래스와 다른 점은 무엇일까? → 인터페이스는 규격만들 위해 태어난 것이다.
그렇기 때문에 인터페이스는 기본이 추상 메소드이고, abstract 키워드가 없이도 추상메소드 선언이 가능하다.
java 1.8 이전에는 인터페이스에서 일반 메소드를 만들지 못했지만 1.8 이후부터는 drfault 키워드 추가 시 일반 메소드 생성이 가능하다. 또한 정적메소드(static method)도 사용 가능하다.
인터페이스는 규격만 있고 실체가 없기 때문에 상속이아닌 구현이라고 표현한다.
구현은 extends가 아닌 implements 키워드를 사용해 구현해준다.
앞에서 다형성에 대해 설명하면서 부모를 상속받은 자식은 부모 타입으로 들어갈 수 있다 했는데 인터페이스도 해당 인터페이스를 구현한 클래스는 인터페이스의 타입으로 들어갈 수 있다.
✔ 정리
1. 인터페이스는 특정 기능을 이용하기 위한 접점 역할을 한다. (규격)
2. 인터페이스는 객체화가 불가능하다.
3. 인터페이스는 기본적으로 추상메서드를 사용한다 (키워드 생략 가능)
4. 인터페이스를 통해 특정 기능 사용에 대한 규격을 설정할 수 있다.
5. 추상 클래스와의 차이는 한번쯤 읽어보자
변수 선언 불가(클래스 변수가 없다. 메소드 안에서는 있지만)
*뒤에 Impl 을 붙여주면 인터페이스를 구현한 클래스임을 작성해주는 규칙이다.
인터페이스와 다형성
interface를 구현받은 클래스는 다형성을 활용할 수 있다.
클래스를 상속 받았다면 부모 클래스 타입으로 객체를 생성할 수 있듯이 인터페이스를 구현했다면 인터페이스 타입으로 객체 생성이 가능하다.
그렇다면 다중 구현을 한 인터페이스의 경우에는 어떻게 해야할까?
만약에 interface A, B를 구현받은 C라는 클래스가 있다면 C는 A의 형태로도 B의 형태로도 들어갈 수 있지만 각각의 interface에 있는 메소드들만 사용할 수 있게 된다.
(다형성은 부모의 내용만 쓸 수 있기 때문이다.)
그래서 각각 부모 타입의 변수를 선언해서 사용해줘야 한다.
(interface를 상속해줘도 문제는 해결되기는 한다!)
// A와 B 인터페이스를 구현한 C클래스를 가정
A a = new C();
a.A인터페이스를 오버라이드한 메소드();
B b = new C();
b.B인터페이스를 오버라이드한 메소드();
❕ 인터페이스 다중 구현이 가능한 이유
클래스는 순차적으로 기능을 extends 해 나가는 위계구조 이기 때문에 다중 상속이 불가능하고, 인터페이스는 필요한 부분을 가져와 implements 하는 모듈 구조 이기 때문에 다중 구현이 가능하다.
익명(Anonymous) 객체
interface는 객체화할 수 없기 때문에 익명객체를 활용한다.
익명 이라는 것은 이름이 없다라는 뜻이다.
이름이 없는 이유는 당연히 이름이 필요한 경우와 이름이 필요 없는 경우가 있기 때문이다.
이름이 필요한 경우에 우리는 이름을 물어보지만 지하철에서 지나가는 사람은 지나치면 다시 마주치지 않기 때문에 이름을 물어보지 않는다.
또한, 만약 나와 옆사람 둘만 있을 때에도 이름이 굳이 필요 없을 것이다.
뭐라고 불러도 상대방을 부르는 것으로 알 수 있기 때문이다.
그래서 익명 객체를 사용하는 경우는 두가지이다.
1. 일회성으로 사용될 경우
2. 단 둘만 있어서 이름을 부르지 않아도 될 경우
그렇다면 인터페이스 자체를 객체로 구현하면 어떻게 될까?
인터페이스를 강제로 객체화하려고 하면 익명객체가 생긴다.
// 1. Common 인터페이스
// 2. 인터페이스를 객체화
Common anony = new Common() {
@Override
public void test2() {
System.out.println("anony inner type2");
}
@Override
public void test1() {
System.out.println("anony inner type1");
}
};
anony.test1();
anony.test2();
인터페이스는 클래스에 기생하게 되는데 인터페이스를 객체로 만들 경우 기생하는 클래스가 없기 때문에 인터페이스를 구현하는 클래스를 즉석으로 만들어준다.
이러한 클래스의 특징에는 2가지가 있다.
{} 마지막에 ;을 붙여 바로 실행할 수 있도록 해준다. ; 세미콜론은 실행문에만 붙어있다.
메소드를 만들 경우 자신을 불러줄 때까지 기다렸다가 호출되기 때문에 ;이 붙어있지 않고, 객체화를 시킬 경우 바로 실행시켜야 하기 때문에 ;이 붙는다.
✔ 정리
1. 인터페이스는 복수개 구현이 가능하다.
2. 인터페이스를 구현한 클래스는 해당 인터페이스 형태에 들어갈 수 있다.
3. 인터페이스를 구현 받는 즉석 객체를 만들 수 있다. (익명객체)
4. 이 객체는 즉석에서 만들어져서 이름이 없다.
5. 그래서 이것을 익명(anonymous) 객체 라고 부른다.
❕ 그래서 추상화란?
추상화는 왜 사용하는가? → 추상화는 규격을 만들기 위해 사용한다.
그렇다면 규격을 만들면 뭐가 좋은가? → 결합도를 감소시킬 수 있다.
그럼 결합도란 무엇인가?
결합도가 낮다는 것은 분리와 합체가 쉬운 것으로 우리 일상에서의 예시는 우리의 신체와 핸드폰 충전기가 있다.
우리의 신체는 결합도가 높기 때문에 다른 사람에게 주려면 많은 어려움이 있지만 휴대폰 충전기는 결합도가 낮기 때문에 어려움 없이 쉽게 줄 수 있다.
(과거에는 안드로이드와 아이폰 충전기 규격이 달라 호환이 불가능했지만 규격을 통일한 후 더욱 쉽게 충전기를 주고 받을 수 있게 되었다.)
코드에서는 인터페이스와 추상클래스가 결합도를 낮출 수 있도록 규격을 만들어주는 역할을 한다.