[Java] 추상 클래스와 인터페이스 (꼭 알아야 하는 내용만 뽑아서 정리)
추상 클래스
앞전에 자바 제어자2 에서 abstract 설명할때 추상 메서드에 대해 설명했다
추상 메서드는 메서드의 본체가 완성되지 않은 미완성 메서드를 말하며
중괄호가 없고 세미콜론 으로 끝나야한다
abstract void abc();
추상메서드를 1개 이상 포함하고 있는 클래스는 반드시 추상 클래스로 정의 되어야 한다
즉 추상 클래스는 메서드의 기능이 정의되어 있지 않은 미완성 메서드가 1개 이상 있다는 의미
abstract class A {
abstract void abc();
void bcd() {
}
}
* 메서드의 완성 기준은 중괄호 이다 만약 중괄호는 있는데 내용이 비었다고
미완성 메서드라고 생각 하면 안된다 무조건 중괄호가 있냐 없냐를 기준으로 말한다
그럼 추상 클래스도 객체를 직접 생성할 수 없고 그 안에 메서드도 추상 메서드인데 왜 쓰는걸까
추상클래스는 붕어빵기계의 부품이라고 보면 된다
붕어빵기계 부품을 가지고 붕어빵을 만들지는 못하지만
부품을 가지고 새로운 붕어빵 기계를 만들어서 붕어빵을 만들면 되는것이다
추상 클래스를 상속받는 자식 클래스는 부모에게 상속 받은 메서드를 반드시 오버라이딩 해야한다
완성된 이든 미완성 이든 부모에게 상속받은 메서드를 자식클래스에서 재정의 하는것을
오버라이딩이라 말하며 이중 부모에게 물려 받은 미완성 메서드를 자식클래스에서 완성하는 것을
특별히 구현한다 라고 말한다
abstract class A {
abstract void abc();
}
class B extends A {
void abc() {
}
}
A a = new B();
B b = new B();
* 추상 메서드가 1개 이상 존재하면 반드시 추상 클래스로 정의 해야한다
그렇다면 반대는 어떨까?
추상 클래스 라면 무조건 추상 메서드가 1개 이상 있어야 할까?
정답은 그럴 필요는 없다 이다
내부에 모두 완성된 메서드가 존재해도 추상 클래스를 정의 할 수는 있다
하지만 추상 클래스는 직접 객체를 생성 못하는데 굳이 그럴 필요가 없는 것이다
추상 클래스 타입의 객체 생성 방법
추상 클래스는 직접 객체를 생성 할 수 없지만 자식 클래스를 생성해 객체를 생성하고
부모 클래스인 추상 클래스 타입으로 선언 할 수 있다
또 다른 방법은 익명 이너 클래스를 사용하는 것이다
컴파일러가 내부적으로 추상 클래스를 상속해 메서드 오버라이딩을 수행한 클래스를 생성하고
그 클래스로 객체를 생성하는 방법이다
이때 내부적으로 생성된 클래스명은 전혀 알 수 없으므로
개발자의 입장에서는 익명 클래스가 되는 것이다
// 추상 클래스에 포함된 추상 메서드 오버라이딩
클래스명 참조 변수명 = new 생성자 () {
};
A a = new A(){
void abc() {
// 추상 메서드의 오버라이딩
}
}
이때 A( )는 클래스 A의 생성자를 호출하는 것이 아니라
컴파일러가 클래스 A를 상속받아 abc( ) 메서드를 오버라이딩한
익명 클래스의 생성자를 호출한다는 것을 의미한다
객체를 여러개 만들어야 하는 상황이라면 자식 클래스를 직접 정의하는 첫번째 방법
한번만 만들어 사용할 객체일 때는 두번째 방법
인터페이스
일상생활에서쓰는 인터페이스 의미는 입출력 방식의 호환성을 의미한다
인터페이스는 내부의 모든 필드가 public static final로 정의 되고
static과 default 메서드 이외의 모든 메서드는 public abstract로 정의된 객체 지향 프로그래밍 요소다
class 키워드 대신 interface 키워드를 사용해 선언한다
interface 인터페이스명 {
public static final 자료형 필드명 = 값;
public abstract 리턴 타입 메서드명();
}
interface A {
public static final int a = 3;
public abstract void abc();
}
인터페이스 내에서 필드와 메서드에 사용할 수 있는 제어자가 확정돼 있어서
필드와 메서드 앞에 제어자를 생략해도 컴파일러가 자동으로 각각의 제어자를 삽입해준다
interface A {
int a = 3;
void abc(); // 메서드에 중괄호 없어도 에러 안뜸
}
//클래스명이나 인터페이스명으로 접근 가능 (Static의 특징)
System.out.println(A.a);
// 값 변경 불가능 (final의 특징)
A.a = 4; // 오류발생
인터페이스 상속
implements 키워드를 사용한다
다중 상속이 가능하다
1개의 클래스가 여러개의 인터페이스를 상속할 때 쉼표 , 로 구분해 나열한다
클래스에서 다중상속을 할 수 없는 이유는 두 부모 클래스에 동일하 이름의 필드 또는 메서드 가 있을때
이를 내려 받으면 충돌이 발생하기 때문이다
하지만 인터페이스에서는 모든 필드가 public static final로 정의되어 있어서 충돌이 발생하지 않는다
메서드도 모두 미완성이어서 어차피 자식 클래스 내부에서 완성해 사용하므로 문제될 것이 없다
클래스 상속과 인터페이스 상속 동시에 가능하고 아래와 같이 표기한다
클래스명 extends 클래스명 implements 인터페이스명, 인터페이스명, 인터페이스명 ...
인터페이스가 인터페이스를 상속할때는 extends를 사용한다
인터페이스 extends 인터페이스
그리고 인터페이스가 클래스를 상속할 수 는 없다
쉽게 정리하면
같은 타입끼리는 extends
다른 타입끼리는 implements
단 인터페이스는 상속 불가능
인터페이스에서 상속을 받을때 주의해야할점
- 인터페이스에는 미완성 메서드가 있기에 자식 클래스에서 꼭 완성 메서드로 만들어줘야 한다
- 완성 메서드인데도 오류가 나는 경우 접근제어자를 확인해줘야한다
인터페이스 메서드는 기본적으로 public으로 지정이 되는데
자식클래스에서 메서드를 완성시키는 과정에서 접근제어자를 맞춰주지 못해서 오류가 날 수있다
-접근 제어자는 인터페이스와 같거나 더 범위가 넓어야 한다
인터페이스의 필요성
인터페이스는 입출력의 호환성을 의미한다
예를 들어 그래픽카드가 있는데 수십곳의 회사가 있다면 전부 메서드 명이나 필드명이 다를것이다
이러면 그래픽카드를 바꿀때마다 수정해주는 일을 해야하는데 이걸 인터페이스를 사용한다면
어떤 회사든 동일한 인터페이스를 상속해서 그래픽카드를 제작하므로 동일한 메서드가 존재할 것이다
다만 메서드 내부의 구현 내용은 서로 다르겠지만 상관없다
개발자 입장에서는 메서드를 가져다 쓰기만 하면 되니깐
자바 API는 자바에서 기능을 제어할 수 있게 만든 인터페이스를 의미한다
디폴트 메서드
자바8 이상부터 등장한 기능인데 인터페이스 내에 완성된 메서드인 디폴트 메서드가 포함 될 수 있다
interface 인터페이스 명{
public default 리턴타입 메서드명 {
// 메서드 내용
}
}
이 메서드를 왜 사용하냐면 만약 A 라는 인터페이스를 100개가 넘는 자식 클래스들이 상속해 쓰고 있다하면
이때 A 인터페이스에 새로운 메서드를 하나 추가하면 나머지 모든 자식 클래스에서 오류가 발생한다
왜냐하면 오버라이딩 하지 않았기 때문에 이를 방지하기 위해 만든것이 default 메서드이다
default메서드가 인터페이스 내부에 속하는 일반 메서드 처럼 동작하기에
자식 클래스에서 부모 인터페이스 내부의 default 메서드도 호출할 수 있다
interfave A {
default void abc() {
System.out.println("A 인터페이스의 abc()");
}
}
class B implements A {
@override
public void abc() {
A.super.abc();
System.out.println("B 클래스의 abc()");
}
}
클래스에서 부모를 호출할때는 super를 바로 했는데 인터페이스에서는 인터페이스 명을 붙인다
이거는 인터페이스가 다중 상속을 할 수 있기 때문이다
정적 메서드
자바 8이후부터 추가된 기능 두번째는 static 메서드를 포함할 수 있다는 것이다
클래스 내부의 정적 메서드와 동일한 기능으로
객체를 생성하지 않고 인터페이스명.정적 메서드명 방식으로 호출할 수 있다
interface A {
static void abc() { }
}
A.abc();