자바

[자바] 인터페이스

j9972 2022. 8. 5. 21:38
728x90

- 일종의 추상클래스이다. ( 추상 클래스 보다 추상화가 더 되어 있다. )

- 오직 추상 메서드와 상수만 멤버로 갖는다 

 

어떤 클래스가 있고 인터페이스 사용시 그 클래스는 반드시 인터페이스에 있는 메소드를 사용하게끔 강제한다.

 

interface I {
    public void z(); // 구현부 (몸통) 이 없어야 한다
}

class A implements I {
    public void z(){} // 구현은 여기서 한다.
}

 

인터페이스 규칙

1. 하나의 클래스는 복수개의 인터페이스 구현 가능

interface I1 {
    public void x(); // 구현 ㄴ
}
interface I1 {
    public void z();
}

class A implements I1, I2 {
    public void x() {} // 구현필수
    public void z() {} // 구현필수
}

 

2. 인터페이스도 상속이 된다

interface I3 {
    public void x(); // 구현 ㄴ
}
interface I4 extends I3 {
    public void z();
}

class A implements I4 {
    public void x() {} // 구현필수
    public void z() {} // 구현필수 
}

 

3. 인터페이스의 멤버는 public 이여야만 한다

 

인터페이스 작성방식

interface 인터페이스이름 {
    public static final 타입 상수이름 = 값;
    public abstract 메서드이름(매개변수 목록);
}
- 모든 멤버변수는 public static final ( 생략가능 ) - 접근제어자 public 명심
- 모든 메서드는 public abstract ( 생략가능 ) - 접근제어자 public 명심

 

- 인터페이스는 다중 상속이 가능하다.

 

인터페이스 구현

- 인터페이스는 자체로 인스턴스를 생성할 수 없다.

 

추상 클래스가 상속을 통해 추상 메서드를 완성하듯 implements를 통해 클래스 작성

class 클래스 이름 implements 인터페이스이름 { }

class Fighter implements Fightable {
    public void move(int x, int y) { /* */ }
    public void attack(Unit u) { /* */ }
}

 

만일, 인터페이스의 메서드 중 일부만 구현하면 abstract   붙여줘야 한다.

abstract class Fighter implements Fightable {
    public void move(int x, int y) { /* */ }
}

 

상속과 구현을 동시에 할 수 있다.

class Fighter extends Unit implements Fightable {
    public void move(int x, int y) { /* */ }
    public void attack(Unit u) { /* */ }
}

 

인터페이스를 이용한 다형성

인터페이스도 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조가능하고, 인터페이스의 타입으로 형변환도 가능하다.

 

인터페이스 Fightable을 클래스 Fighter가 구현했을때

Fightable f = new Fighter();
// Fightable 이라는 인터페이스
// Fighter 이라는 클래스

 

메서드의 매개변수의 타입도 가능

void attack(Fightable f) { }

 

인터페이스 타입의 매개변수가 갖는 의미는 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야한다는것이다

Fiahtable method() {
    Fighter f = new Fighter();
    return f;
    
    // 위의 두줄을 윗말대로 줄여보면
    // return new Fighter(); // 인스턴스를 반환하고 있다
}

 

 

class Fighter extends Unit implements Fightable {
    void attack(Fightable f) { }
}

 

정말 중요한것!

Fightable method() {
    Fighter f = new Fighter();
    return f;
}

리턴 타입이 인터 페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.

 

interface Parseable {
    public static void parse(String fileName);
}

class ParserManager {
    // 리턴타입이 Parseable 인터페이스이다.
    public static void main(String[] args) {
        if( type.equals("XML")) {
            return new XMLParser();
        } else {
            Parseable p = new HTMLParseable();
            return p;
        }
    }
}

class XMLParser implements Parseable {
    public void parse(String fileName) {
        System.out.println(fileName);
    }
}

class HTMLParser implements Parseable {
    public void parse(String fileName) {
        System.out.println(fileName);
    }
}

class ParserTest {
    public static void main(String[] args) {
        Parseable parser = ParserManager.getParser("XML");
        parser.parse("xml");
        parser = ParserManager.getParser("HTML");
        parser.parse("html");
    }
]

getParser 매서드는 매개변수로 넘겨받는 type에 따라 값이 XMLParser 인스턴스 또는 HTMLParser 인스턴스 반환한다

이렇게 하면, 프로그램 자체를 변경하지 않고, 서버측의 변경만으로 사용자가 새로 개전된 프로그램을 사용할 수 있다.

 

인터페이스 이해

직접적인 관계

A( User ) -> B ( Provider ) : 직접적인 관계 , B가 바뀌면 A도 바뀌어야함
class A {
    public void method(B b) {
        b.methodB();
    }
}

class B {
    public void method() {
        System.out.println("method b");
    }
}

class InterfaceTest {
    public static void main(String[] args) {
        A a = new A();
        a.methodA(new B());
    }
}

 

 

간접적인 관계로 바꾸기 위해서는 인터페이스를 통해 클래스 B의 선언과 구현 분리 필요

간접적으로 'A-I-B' 관계 : A는 인터페이스의 영향만 직접적으로 받는다 ( B의 영향을 받지 않는다.)
class A {
    public void method(I i) { // 이 부분이 중요
        i.methodB(); // 이 부분이 중요
    }
}

interface I {
    public abstract void methodB();
}

class B implements I {
    public void method() {
        System.out.println("interface method b");
    }
}

class InterfaceTest {
    public static void main(String[] args) {
        A a = new A();
        a.methodA(new B());
    }
}

 

인스턴스를 직접 생성하지 않고, getInstance() 라는 메서드를 통해 제공 받는다 

나중에 다른 클래스의 인스턴스로 변경되어도 A클래스의 변경없이 getInstance()만 변경하면 된다는 장점이 있다

class InterfaceTest {
    public static void main(String[] args) {
        A a = new A();
        a.methodA();
    }
}

class A {
    void method() {
        I i = InstanceManager.getInstance();
        i.methodB();
        System.out.println(i.toString());
    }
}

interface I {
    public abstract void methodB();
}

class B implements I {
    public void method() {
        System.out.println("interface method b");
    }
    public String toString() { return "class B" }
}

class InstanceManager {
    public static I getInstace() {
        return new B(); // 다른 인스턴스로 바꾸려면 여기만 바꾸면 된다
    }
}

인터페이스 I 타입의 참조변수 i로도 Object 클래스에 정의된 메서들을 호출 가능하다 ( toString() 이런 메서드 )