인터페이스와 다형성

김형준 Kim Hyeong Jun·2022년 12월 4일
0
post-thumbnail

인터페이스

자바에서 인터페이스(Interface)는 객체의 사용방법을 정의한 타입이다.
인터페이스를 통해 다양한 객체를 동일한 사용 방법으로 이용할 수 있다.

인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 한다.

  1. 개발 코드가 인터페이스의 메소드를 호출하면
  2. 인터페이스는 객체의 메소드를 호출시킨다.

왜 중간에 인터페이스를 두는걸까?

그 이유는 개발 코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해서이다.
인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다.
따라서 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화할 수 있다는 장점을 가지게 된다.

인터페이스 선언

인터페이스 선언은 class 키워드 대신에 interface 키워드를 사용한다.

public interface 인터페이스이름 {---}

구성 멤버로는

  1. 상수 필드
  2. 추상 메소드

만을 가진다.

인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.

상수 필드 선언

인터페이스는 객체 사용 방법을 정의한 것이므로
실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를 선언할 수 없다.
그러나 상수 필드(constant field)는 선언이 가능하다.
(상수는 인터페이스에 고정된 값으로 실행 시에 데이터를 바꿀 수 없다.

public static final 타입 상수이름 =;

상수 이름은

  1. 대문자로 작성
  2. 서로 다른 단어로 구성되어 있을 경우 언더바(_)로 연결

하는 것이 관례이다.

추상 메서드 선언

인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다.
그렇기 때문에 인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언한다.

public abstract 리턴타입 메소드이름(매개변수);

인터페이스 구현

객체는 인터페이스에서 정의된 추상 메서드와 동일한 메서드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다.
이러한 객체를 인터페이스의 구현(implement) 객체라고 하고,
구현 객체를 생성하는 클래스를 구현 클래스라고 한다.

구현 클래스

보통의 클래스와 동일하다.
단, 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에
implements 키워드를 추가하고 인터페이스 이름을 명시해야 한다.
그리고 인터페이스에 선언된 추상 메서드의 실체 메서드를 선언해야 한다.

public class 구현클래스이름 implements 인터페이스이름 {
	//인터페이스에 선언된 추상 메서드의 실체 메서드 선언
}

public 접근 제한
인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에
public 보다 더 낮은 접근 제한으로 작성할 수 없다.
public을 생략하면 "Cannot reduce the visibility of the inherited method" 컴파일 에러가 뜬다.

구현 클래스가 작성되면 new 연산자로 객체를 생성할 수 있다.

Television tv = new Television();

하지만 위 코는 인터페이스를 사용한 것이 아니다.

인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.

RemoteControl rc = new Television();

다중 인터페이스 구현 클래스

객체는 다음 그림과 같이 다수의 인터페이스 타입으로 사용할 수 있다.

두 가지의 인터페이스가 객체의 메소드를 호출할 수 있으려면
객체는 이 두 인터페이스를 모두 구현해야한다.

public class 구현클래스이름 implements 인터페이스A, 인터페이스B {
	// 1번 인터페이스에 선언된 추상 메서드의 실체 메서드 선언
    // 2번 인터페이스에 선언된 추상 메서드의 실체 메서드 선언
}

위와 같이 다중 인터페이스를 구현할 경우,
구현 클래스는 모든 인터페이스의 추상 메서드에 대해 실체 메서드를 작성해야 한다.

인터페이스 사용

구현 객체가 인터페이스 변수에 대입된다는 것을 알았다.
이제부터 인터페이스로 구현 객체를 사용하는 방법을 알아보자.

public class MyClass {
	//필드
	RemoteControl rc = new Television();
    
    //생성자
    MyClass(RemoteControl rc) {
    	this.rc = rc;
    }
    
    //메소드
    void methodA() {
    	//로컬 변수
    	RemoteControl rc = new Audio();
    }
    
    void methodB(RemoteControl rc) {---}
}
  1. 인터페이스가 필드 타입으로 사용될 경우, 필드에 구현 객체를 대입할 수 있다.
  2. 인터페이스가 생성자의 매개 변수 타입으로 사용될 경우,
    new 연산자로 객체를 생성할 때 구현 객체를 생성자의 매개값으로 대입할 수 있다.
  3. 인터페이스가 로커 변수 타입으로 사용될 경우, 변수에 구현 객체를 대입할 수 있다.
  4. 인터페이스가 메소드의 매개 변수 타입으로 사용될 경우, 메소드 호출 시 구현 객체를 매개값으로 매개값으로 대입할 수 있다.

인터페이스의 사용 부분은 실제 실습을 통해서 이해하는게 좋을 것 같다.

타입변환과 다형성

다형성을 구현하기 위해서는 메소드 재정의와 타입 변환이 필요하다.
인터페이스 역시 상속과 마찬가지로 두 가지 기능이 제공되므로 다형성을 구현하는데 많이 사용된다.
상속은 같은 종류의 하위 클래스를 만드는 기술,
인터페이스는 사용 방법이 동일한 클래스를 만드는 기술이라는 개념적 차이가 있지만, 다형성을 구현하는 방법은 비슷하다.

자동 타입 변환

구현 객체가 인터페이스 타입으로 변환되는 것은 자동 타입 변환에 해당한다.

필드와 매개 변수의 타입을 인터페이스로 선언하면 다양한 구현 객체를 대입해서 실행결과를 다양하게 만들 수 있다.

필드의 다형성

상속에서 소개했던 그림과 비슷하다.
차이점은 Tire 가 클래스 타입이 아니고, 인터페이스 타입이라는 점.

매개 변수의 다형성

매개값을 다양화하기 위해서 상속에서는 매개 변수를 부모 타입으로 선언하고 호출할 때에는 자식 객체를 대입했다.
이번에는 매개 변수를 인터페이스 타입으로 선언하고 호출할 때에는 구현 객체를 대입한다.

인터페이스는 메소드의 매개 변수로 많이 등장한다.
매개 변수의 타입이 인터페이스일 경우 어떠한 구현 객체도 매개값으로 사용할 수 있고, 어떤 구현 객체가 제공되느냐에 따라 메소드의 실행결과는 다양해질 수 있다.

강제 타입 변환

구현 객체가 인터페이스 타입으로 자동 타입 변환하면,
인터페이스에 선언된 메소드만 사용 가능하다는 제약 사항이 따른다.

경우에 따라서 구현 클래스에 선언된 필드와 메소드를 사용하기도 하는데,
이때 강제 타입 변환을 해서 다시 구현 클래스 타입으로 변환한 다음, 구현 클래스의 필드와 메소드를 사용할 수 있다.

객체 타입 확인

강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능하다.
하지만 어떤 구현 객체가 변환되어 있는지 알지도 못하는 상태에서 무작정 강제 타입 변환할 경우 ClassCastException이 발생하게 된다.

Vehicle vehicle = new Taxi();
Bus bus = (Bus) vehicle;
// ClassCastException

우리는 상속에서 객체 타입을 확인하기 위해 instanceof 연산자를 사용했다.
해당 연산자는 인터페이스 타입에서도 사용할 수 있다.

public Class Driver {
	public void drive(Vehicle vehicle) {
    	if(vehicle instanceof Bus) { // vehicle a매개 변수가 참조하는 객체가 Bus 인지 조사
        	Bus bus = (Bus) vehicle;
            bus.checkFare(); 
        }
        vehicle.run();
    }
}

인터페이스 상속

인터페이스도 다른 인터페이스를 상속할 수 있다.
인터페이스는 클래스와는 달리 다중 상속을 허용한다.

public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2 {---}

이경우, 하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드뿐만 아니라 상위 인터페이스의 모든 추상 메소드에 대한 실체 메소드를 가지고 있어야 한다.

하위인터페이스 변수 = new 구현클래스();
상위인터페이스1 변수 = new 구현클래스();
상위인터페이스2 변수 = new 구현클래스();

위 처럼 구현클래스는 하위 및 상위 인터페이스 타입으로 변환이 가능하다.

하위 인터페이스로 타입 변환이 되면 상위 및 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있으나,
상위 인터페이스로 타입 변환되면 상위 인터페이스에 선언된 메소드만 사용이 가능하다.

profile
I want be a developer🙂

0개의 댓글