1. 인터페이스의 정의와 특징
- 내부의 모든 필드가 public static final로 정의
- static과 default 메서드 이외의 모든 메서드는 public abstract로 정의
- interface 키워드 사용하여 선언
- 인터페이스 구조
interface 인터페이스명 {
public static final 자료형 필드명 = 깂;
public abstract 리턴 타입 메서드명();
}
interface A {
public static final int a = 3;
public abstract void abc();
}
- 제어자를 명시적으로 적어 주지 않으면 컴파일러가 자동으로 각각의 제어자 삽입
-> 메서드명 뒤에 중괄호가 없는데도 오류가 발생하지 않으므로 abstract가 자동으로 붙음
-> static : 클래스명으로 바로 접근 가능
-> final : 값이 입력된 후 다시 값을 입력할 수 없음
System.out.println(A.a); //클래스명이나 인터페이스명으로 접근가능(Static의 특징)
A.a=4; //값변경 불가능 (fianl 특징) 오류발생
2. 인터페이스의 상속
- 클래스가 클래스를 상속할 때 extends 키워드 사용
- 클래스가 인터페이스를 상속할 때 implements 키워드 사용
- 다중 상속 가능
-1개의 클래스가 여러 개의 인터페이스를 상속할 때 쉼표(,)로 구분해 나열
클래스명 implements 인터페이스명, ..., 인터페이스명 {
//내용
}
- 클래스에서 다중상속을 할 수 없는 이유
-> 두 부모 클래스에 동일한 이름의 필드 또는 메서드가 존재할 때 이를 내려받으면 충돌이 발생 (ambiquous error)
- But 인터페이스에서는 모든 필드가 public static final로 정의돼 있어 실제 데이터값은 각각의 인터페이스 내부에 존재(저장 공간이 분리)해 공간상 겹치지 않음
- 메서드 또한 모두 미완성이여서 자식 클래스 내부에서 완성해 사용
- 클래스와 인터페이스를 동시에 상속하는 구조
클래스명 extends 클래스명 implements 인터페이스명, ..., 인터페이스명 {
//내용
}
package Interface;
interface A {}
interface B {}
//단일 인터페이스 상속
class C implements A {
}
//다중 인터페이스 상속
class D implements A,B {
}
//클래스와 인터페이스를 한 번에 상속
class E extends C implements A,B {
}
public class Interface1 {
public static void main(String[] args) {
}
}
1) 클래스가 클래스를 상속할 때는 extends
클래스 extends 클래스 {
//...
}
2) 클래스가 인터페이스를 상속할 때는 implements
클래스 implments 인터페이스 {
//...
}
- '클래스 A가 인터페이스 B를 상속했다.' = ' 클래스 A가 인터페이스 B를 구현했다'
- 인터페이스 내의 모든 메서드는 추상 메서드이므로 자식 클래스는 온전히 이들 추상메서드를 구현
3) 인터페이스가 인터페이스를 상속할 때는 extends
인터페이스 extends 인터페이스 {
//...
}
4) 인터페이스는 클래스를 상속할 수 없음
- 클래스 내부에는 완성된 메서드가 존재
- 인터페이스는 내부에 추상 메서드만 포함할 수 있으므로 만일 인터페이스가 클래스를 상속한다면 상속과 동시에 오류 발생
주의점 1. 자식 클래스는 반드시 미완성 메서드를 완성시켜 줘야 문법적 오류를 피할 수 있음
interface A {
public abstract void abc(); //미완성 메서드
}
class B implements A {
public void abc() { // 완성 메서드
}
}
주의점 2. 자식 클래스의 구현 메서드는 public만 가능
- 오버라이딩을 수행할 때 접근 지정자는 반드시 부모 메서드의 접근 지정자보다 접근 범위가 같거나 커야함
interface A {
void abc(); //컴파일러가 자동으로 public abstract 제어자 추가
}
class B implements A {
void abc() { //default 접근 지정자를 가지는 메서드로 오버라이딩 -> 접근범위가 좁아져 오류 발생
}
}
3. 인터페이스 타입의 객체 생성 방법
- 인터페이스도 추상 메서드를 포함하고 있으므로 객체를 직접 생성할 수 없음
1) 자식 클래스를 직접 정의해 인터페이스 객체 생성
package Interface;
interface A{
int a = 3;
void abc();
}
class B implements A {
public void abc() {
System.out.println("방법 1. 자식 클래스 생성자로 객체 생성");
}
}
public class Interface11 {
public static void main(String[] args) {
//객체 생성
A b1 = new B();
A b2 = new B();
//메서드 호출
b1.abc();
b2.abc();
}
}
2) 익명 이너클래스를 활용해 인터페이스 객체 생성
package Interface;
interface A{
int a = 3;
void abc();
}
public class Interface11 {
public static void main(String[] args) {
//객체 생성
A a1 = new A() {
public void abc() {
System.out.println("방법 2. 익명 이너 클래스를 이용한 객체 생성");
}
};
A a2 = new A() {
public void abc() {
System.out.println("방법 2. 익명 이너 클래스를 이용한 객체 생성");
}
};
//메서드 호출
a1.abc();
a2.abc();
}
}
4. 인터페이스의 필요성
- 인터페이스 = 입출력의 호환성
- ex) 그래픽 드라이버
A사의 그래픽 카드와 B사의 그래픽 카드는 서로 다른 하드웨어 구조를 띠고 있음
1) 인터페이스 사용 X
- A 사의 그래픽 카드를 활용한 애플리케이션을 작성한 후 그래픽 카드를 B사 제품으로 바꾸면 애플리케이션을 수정해야함
2) 인터페이스 사용
- 애플리케이션에서 인터페이스를 활용해 프로그램을 작성, 각 그래픽 회사들은 이 인터페이스를 구현한 클래스를 생성
- 어떤 그래픽 회사이든 동일한 인터페이스를 상속해 드라이버를 제작하므로 모든 드라이버에는 동일한 메서드가 존재
- 각 그래픽 드라이버가 인터페이스를 구현했으므로 그저 그 메서들 가져다 쓰면 되는 것
5. 디폴트 메서드와 정적 메서드
- 자바 8 등장 후 인터페이스의 기능 추가
1) 인터페이스 내에 완성된 메서드인 디폴트(default)메서드가 포함
- 디폴트 메서드
interface 인터페이스명 {
public default 리턴 타입 메서드명() {
//메서드 내용
}
}
- 디폴트 메서드 앞에 접근 지정자 public을 생략해도 컴파일러가 자동으로 삽입
- ex) 인터페이스 A를 클래스 100개에서 상속
- 인터페이스 내에 메서드를 1개 더 추가하면 이전에 만들어 사용하던 모든 자식 클래스에서 오류 발생
(새롭게 추가된 메서드를 구현하지 않았기 때문)
- 디폴트 메서드 즉. 인터페이스 내부에 완성된 메서드를 삽입하면 문제 해결
- 디폴트 메서드는 완성된 메서드이므로 자식 클래스는 이 메서드를 반드시 오버라이딩할 의무가 없음
package Interface;
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() { // 인터페이스 A의 디폴트 메서드까지 오버라이딩
System.out.println("C 클래스의 bcd()");
}
}
public class DefaultMethod {
public static void main(String[] args) {
//객체 생성
A a1 = new B();
A a2 = new C();
//메서드 호출
a1.abc();
a1.bcd();
a2.abc();
a2.bcd();
}
}
- 디폴트 메서드는 일반 메서드 처럼 자식 클래스에서 오버라이딩해 사용 가능
- 자식 클래스에서 부모 인터페이스의 디폴트 메서드 호출 방법
부모 인터페이스명 super. 디폴트 메서드명
- 디폴트 메서드가 인터페이스 내부에 속하는 일반 메서드처럼 동작한다고 했으므로 자식 클래스에서 부모 인터페이스 내부의 디폴트 메서드도 호출 가능
- 인터페이스는 다중 상속이 되므로 어떤 부모 인터페이스 내부의 메서드를 호출하라는 소리인지 구분할 필요가 있음
package Interface;
interface A {
default void abc() {
System.out.println("A 인터페이스의 abc()");
}
}
class B implements A {
public void abc() {
A.super.abc(); //부모 인터페이스 A의 abc() 메서드호출
System.out.println("B 클래스의 abc()");
}
}
public class DefaultMethod {
public static void main(String[] args) {
//객체 생성
B b = new B();
//메서드 호출
b.abc(); //B 객체의 abc()를 호출할 때 A 인터페이스의 abc() 메서드가 먼저 호출됨
}
}
2) Static 메서드를 포함할 수 있다.
- 클래스 내부의 정적 메서드와 동일한 기능
- 객체를 생성하지 않고 '인터페이스명.정적 메서드명' 방식으로 바로 호출 가능
package Interface;
interface A{
static void abc() {
System.out.println("A 인터페이스의 정적 메서드 abc()");
}
}
public class DefaultMethod {
public static void main(String[] args) {
//정적 메서드 호출
A.abc();
}
}
'Java' 카테고리의 다른 글
13 - 2 장 익명 이너 클래스 (0) | 2023.06.05 |
---|---|
13 -1 장 이너 클래스 (0) | 2023.06.04 |
12-1장 추상 클래스 (0) | 2023.06.02 |
11-2장 abstract 제어자 (0) | 2023.06.02 |
11-1장 final 제어자 (0) | 2023.06.02 |