본글은 'Do it 자바 완전 정복' 책을 통한 공부내용을 정리한 글입니다.
추상 클래스
추상 메서드(abstract method)는 메서드의 본체가 완성되지 않은 미완성 메서드를 말한다. 메서드의 기능을 정의하는 중괄호 자체가 없으며, 고로 명령어의 끝을 알리는 세미콜론(;)으로 끝나야 한다.
추상 메서드를 1개 이상 포함하고 있는 클래스는 반드시 추상 클래스(abstract class)로 정의되어야 한다. 추상 클래스의 형식 또한 추상 메서드 구문과 비슷하게 class 앞에 abstract를 붙여 표현한다.
abstract class A {
abstract void abc();
void bcd() {
// ...
}
}
추상 클래스의 특징
추상 클래스는 내부의 미완성 메서드 떄문에 객체를 직접 생성할 수 없다. 그러나 이 추상 클래스를 상속한 자식 클래스를 생성하면 그 자식 클래스로 객체를 생성할 수 있다. 그렇게 생성된 객체 내부에는 부모 클래스의 추상 메서드가 구현되어 있다. 부모에게 물려받은 미완성 메서드를 자식 클래스에서 완성하는 것을 '구현한다(implements)'라고 한다.
abstract class A {
abstract void abc();
}
class B extends A {
void abc() {
// ...
}
}
public class AbstractClass{
public static void main(String[] args) {
A a = new A(); // 추상 클래스 이므로 객체생성 불가능
A a = new B(); // o
B b = new B(); // o
}
}
추상 클래스 타입의 객체 생성 방법
위에서 언급했던 추상 클래스의 객체 생성 방법 말고 다른 방법을 알아보자. 두 번째 방법은 익명 이너 클래스를 사용하는 것이다. 이는 컴파일러가 내부적으로 추상 클래스를 상속해 메서드 오버라이딩을 수행한 클래스를 생성하고, 그 클래스로 객체를 생성하는 방법이다. 익명 이너 클래스의 문법 구조는 다음과 같다
A a = new A() {
void abc() {} // 추상 메서드의 오버라이딩
}
이때 A()는 클래스 A의 생성자 호출이 아닌 abc() 메서드를 오버라이딩한 익명 클래스의 생성자를 호출한다는 것을 의미한다. 두번째 방법을 활용하면 추가로 자식 클래스를 정의하지 않아도 되고, 코드가 간결해 보이는 장점이 있다. 그러나 객체를 여러번 생성해야 하는 경우 그때마다 익명 클래스를 정의해야 한다.
abstract class A {
abstract void abc();
}
public class AbstractClass2 {
public static void main(String[] args) {
A a1 = new A() {
void abc() {
System.out.println("이너클래스1");
}
}
A a2 = new A() {
void abc() {
System.out.println("이너클래스2");
}
}
a1.abc();
a2.abc();
}
}
정리해, 객체를 여러 개 만들어야 하는 상황이라면 자식 클래스를 정의하는 것이 좋다. 하지만 딱 한 번만 사용할 객체일 때는 익명 이너 클래스를 활용하는 것이 간결한 코드 작성에 도움이 된다.
인터페이스
인터페이스는 내부의 모든 필드가 public static final
로 정의되고, static과 default 메서드 이외의 모든 메서드는 public abstract
로 정의된 객체 지향 프로그래밍 요소로 class 키워드 대신 interface 키워드를 사용해 선언한다
interface 인터페이스명 {
public static final 자료형 필드명 = 값;
public abstract 리턴 타입 메서드명();
}
인터페이스 내에서 필드와 메서드에 사용할 수 있는 제어자(modifier)가 확정되어 있으므로 필드와 메서드 앞에 제어자를 생략시에도 컴파일러가 자동으로 각각의 제어자를 삽입한다.
인터페이스의 상속
클래스가 클래스를 상속할 때 extends 키워드를 사용했었다. 클래스가 인터페이스를 상속 할 때는 implements
키워드를 사용한다. 인터페이스의 가장 큰 특징은 다중 상속이 가능하다는 것이다. 클래스에서 다중 상속을 할 수 없었던 이유는 두 부모 클래스의 동일한 이름의 필드 또는 메서드가 존재 하면 충돌이 발생했기 때문이었다. 그러나 인터페이스에서는 모든 필드가 public static final로 정의되어 있어 실제 데이터값을 각 인터페이스 내부에 존재해 저장공간이 겹치지 않기 때문이다. 클래스와 인터페이스를 함께 상속할 때에는 아래의 구조를 꼭 지켜야한다.
클래스명 extends 클래스명 implemetns 인터페이스명, ...., 인터페이스명 {
// 내용
}
인터페이스가 인터페이스를 상속할 때는 extends를 사용한다. 인터페이스의 내부에는 완성된 메서드가 들어갈 수 없으므로 구현할 수 없는 것이다. 또한, 인터페이스는 클래스를 상속할 수 없다. 클래스 내부에는 완성된 메서드가 존재하기 때문이다.
자식클래스에서 부모 인터페이스의 추상 메서드를 오버라이딩 할 때 주의할 점이 있다. 인터페이스는 모든 필드,메서드가 public으로 강제되므로 자식 클래스에서 구현될 때 public 접근 지정자만 사용 가능하다. 접근 지정자를 표기하지 않았을 경우 default가 기본으로 지정되므로 주의하자.
인터페이스 타입의 객체 생성 방법
인터페이스도 추상 메서드를 포함하고 있으므로 객체를 직접 생성할 수 없다. 추상 클래스와 마찬가지로 자식 클래스를 정의하고, 자식 클래스의 생성자로 객체를 생성하는 방법과 익명 이너 클래스를 이용해 바로 객체를 생성하는 방법을 사용할 수 있다. 각 방법에서의 장단점 또한 추상클래스와 같다.
인터페이스의 필요성
그래픽 드라이버로 예를 들어본다. A사와 B사의 그래픽카드는 서로 다른 하드웨어 구조를 띠고, 기능을 구현하는 방법또한 다르다. 이때 인터페이스를 사용하지 않을 경우 수백 종의 어플리케이션을 만들어야 할 것이다. 인터페이스를 활용해 프로그램을 작성하면, 각 그래픽 회사들은 해당 인터페이스를 구현한 클래스를 생성한다. 메서드 내부의 구현 내용은 서로 다르지만 해당 애플리케이션을 작성하는 개발자의 입장에서는 전혀 고려할 필요가 없는 것이다.
디폴트 메서드와 정적 메서드
자바 8이 등장하면서 인터페이스 내에 완성된 메서드인 default 메서드가 포함될 수 있게 되었다. 디폴트 메서드는 이미 완성된 메서드이므로 자식 클래스는 해당 메서드를 오버라이딩 할 의무가 없어진다. 디폴트 메서드는 일반 메서드처럼 자식 클래스에서 오버라이딩해 사용할 수도 있다.
interface A {
void abc();
default void bcd() {
System.out.println("A인터페이스의 bcd");
}
}
class B implements A {
public void abc () {
System.out.println("B클래스의 abc");
}
}
class C implements A {
public void abc () {
System.out.println("C클래스의 abc");
}
public void bcd () {
System.out.println("C클래스의 bcd");
}
}
public class DefaultMethod {
public static void main(String[] args) {
A a1 = new B();
A a2 = new C();
a1.abc(); // B클래스의 abc
a1.bcd(); // A인터페이스의 bcd
a2.abc(); // C클래스의 abc
a2.bcd(); // C클래스의 bcd
}
}
자식 클래스에서 부모 인터페이스 내부의 디폴드 메서드도 호출할 수 있다. 방법은 다음과 같다.
부모 인터페이스명.super.디폴트 메서드명
자바 8 이후로 추가된 인터페이스의 두 번째 기능은 static 메서드를 포함할 수 있다는 것이다. 이는 클래스 내부의 정적 메서드와 동일한 기능이다. 객체를 생성하지 않고 '인터페이스명.정적 메서드명'의 방식으로 바로 호출할 수 있다.
interface A {
static void abc() {}
}
A.abc();
'Backend > JAVA' 카테고리의 다른 글
[Java] #14 예외 처리 (0) | 2023.06.30 |
---|---|
[Java] #13 이너 클래스와 이너 인터페이스 (1) | 2023.06.06 |
[Java] #11 자바제어자 2 - final, abstract (0) | 2023.06.01 |
[Java] #9 자바 제어자 1 - 접근지정자, static (0) | 2023.05.28 |
[Java] #8 클래스 외부 구성 요소 (0) | 2023.05.25 |