JAVA

인터페이스란?

yuno82 2023. 1. 13. 13:24

인터페이스란 객체간 사용방법을 정의한 타입을 인터페이스라고 한다.(추상메서드의 집합)

인터페이스는 단일객체가 아닌 여러 객체들과 사용이 가능하기에 (한번 선언해두면 필요할때마다 가져와서 사용할수 있기 때문에) 코드의 수정이 필요할경우 사용하는 객체 (파라미터)만 변경하면 다른 값이 반환시키는것(코드의 종속성을 낮추는것)이 주요 목적이다.

 

또한 인터페이스가 추상메서드만 갖게 된 이유는 기존 메서드와의 충돌로 인하여 이를 없애고자 추상메서드만 갖도록 정한것이다. 


인터페이스의 다형성 

//다형성예시 코드
//추상메서드
abstract class Unit{	
    int x, y;
    abstract void move (int x, int y);
    void stop() {System.out.println("멈춤")};
}

//인터페이스 선언
interface Fightable{	
    void move(int x, int y);	//public abstract 생략(추상메서드)
    void attack(Fightable f);	//public abstract 생략(추상메서드)
}

//인터페이스 구현(오버라이딩) 
class Fighter extnes Unit implments fightable{
    //오버라이딩 규칙 : 조상보다 접근제어자가 낮으면 안된다.
    //인터페이스 선언 코드의 접근제어자는 public이다
    
    pbulic void move(int x, int y) {
    	System.out.println(x+","+y+"위치로 이동");
    }
    pbulic void attack(Fightable f) {
    	System.out.println(f+"를 공격");	
    } 	
}

//메인함수
public class FighterTest{
	public static void main(String[] args){
    	Fighter f= new Fighter();
        f.move(100,200);
        f.attack(new Fighter());
    }
}

다형성 코드에서 주목해서 볼것은 Fightable(인터페이스)가 Fighter(자손객체) 를 참조해서 받는모습을 볼수있는데 이것은 상속과 다형성을 설명하면서 이야기하겠다.

이전에 배운 상속은 부모클래스가 자식클래스에게 맴버를 물려주는것을 상속이라고 하고있는데 (A)같은 경우는 매개변수 타입이 인터페이스인경우 이것을 구현한 클래스(자손클래스)의 인스턴스만 사용이 가능하다.(사용 가능한 메서드는 move와attack 2개이고 stop은 사용 불가능하다. -> 부모님이 주신 유산을 받은 자식도, 물려준 부모도 쓸수 있되 자식이 산건 못쓴다고 생각하면 된다.)

 

//부모의 자손참조
Unit u = new Fighter();		//조상클래스의 자손객체참조	
Fightable f = new Fighter();	//인터페이스의 자손객체참조   (A)

//단, f는 상단의 Fightable의 메서드만 사용이 가능하다.
//즉,f.move(100,200); OR f.attack(new Fighter()); 2개는가능 f.stop();는 불가능

다형성은 자식객체가 부모객체로 자동변환(Casting)이 가능하다는 특징이 있는데 인터페이스에서 이러한 예시를 보여주는것이 (A)이다.

하단의 주석처리를 해놨듯이 부모클래스는 자식클래스에서 구현된것만 사용가능하다.

조금더 알기쉽게 설명하자면 포유류(부모) / 박쥐,고래(자식)으로 생각하고 하단의 사진을 보길 바란다.

포유류인 고래는 헤엄을 칠수 있고 박쥐는 날수 있다.(부모클래스를 자식클래스에게 상속)

박쥐는 포유류이며 날수있다 하지만 헤엄은 못친다.(자식클래스를 부모클래스로 변환)

고래는 포유류이며 헤엄칠수 있다. 하지만 날수는 없다. (자식클래스를 부모클래스로 변환)

이런식으로 이해할수 있다.

 

//(B)
Fightable method(){
	....//구현내용
    Fighter f = new Fighter();	//여기서부터
    return f;			//여기까지 한줄로 줄여 쓰면 return new Fighter(); 이다.
}

(B)는 메서드 반환타입이 인터페이스면 반환받는 변수의 타입도 인터페이스이어야 한다.

이것은 인터페이스의 구현한 객체를 반환해야한다 -> 인터페이스가 아닌 추상메서드, 객체등을 반환할수 없기 떄문이다.

(A)에서 나타나 있듯이 메서드의 반환타입이 인터페이스면 반환받는 변수도 인터페이스이어야 한다.

여기서도 자식객체(retutn f)가 부모객체(Fightable)로 자동변환(Casting)이 되기 때문에 해당코드를 실행해도 문제없는 모습을 확인할수 있다.


인터페이스 선언맴버

//인터페이스 선언 방법
//interface 인터페이스 이름

interface PlayingCard{
    //상수필드 : 타입 상수명 = 값;
    public static final int SPADE = 5;	
    final int DIAMOND = 3;				//public static final int로 자동 치환
    static int HEART = 2;				//public static final int로 자동 치환
    int CLOVER = 1;					//public static final int로 자동 치환
    
    //추상메서드 : 타입 메소드명(매개변수,..);
    public abstract String getCardNumber();
    String getCardKind();				//public abstract String getCardkind로 자동 치환

    //디폴트 메서드 : default 리턴타입 메소드명 (매개변수,..) {...내용...}
    default void setCard(boolean gameplay){
    	if(gameplay){
        System.out.println("게임 시작");
        } else {
        System.out.println("게임 종료");
        }    
    }
    
    //정적메서드 : static 타입 메소드명(매개변수) {..내용..};
    static void changeSatge(){
    	System.out.println("스테이지 변경");
    }
}

상단은 인터페이스의 예시인데 인터페이스의 선언에서 허용되는건 다음과 같다.

1.상수필드 - 상단에 기술한대로 인터페이스는 데이터를 저장할수 없고 추상 메서드의 집합이므로 추상메서드의 규칙에 반하지 않는 값이 고정된 상수는 사용이 가능하다 

2.추상메서드 - 메서드인데 내용( {..내용..} ) 이 없는 메서드 (내용은 구현단계에서 오버라이드로 구현한다)

3.디폴트메서드 - 기존 인터페이스를 확장하여 새로운 기능을 추가하기 위한 목적이다. (실행내용을 작성한것)

4.정적메서드 - 객체가 없어도 인터페이스 호출이 가능하다 

(인터페이스 내부의 필드는 자동적으로 public static final이 접두로 붙는다 -> 그래야 추상메서드의 목적인 다른메서드에서 참조가 가능하니까)


추상클래스와 인터페이스 차이점

추상클래스 - 일반 클래스 인데 추상메서드를 갖고있는것 (일부 미완성)

인터페이스 - 추상메서드만 갖고있는것 (완전 미완성 - 구현된게 없음)

 

만일 하단의 코드처럼 추상클래스의 attack 메서드가 미구현된 경우 (인터페이스에서 일부만 구현하는 경우) 추상클래스로 인식이 되며 앞쪽에 abstract를 붙여준다.

//인터페이스 예시
interface Play_Unit{
	void move(int x, int y);	
	void attack(string m);	//public abstract 생략됨
}


//추상클래스 예시)	(B)
abstract class Play_Unit2 implements Play_Unit{
    public void move(int x, int y){
        System.out.println("캐릭터가"+x+"위치에서"+y+"위치로 이동합니다");}	//구현됨
    public void attack(string m);	//미구현됨

}


//인터페이스 구현
//class 클래스명 implements 인터페이스 이름{..}

class Unit implements Play_Unit{
	public void move(int x, int y){
    	System.out.println("캐릭터가"+x+"위치에서"+y+"위치로 이동합니다");	//구현완료
    }
    
	public void attack(string m){
    	System.out.println("캐릭터가+m+"를 공격합니다.");	//구현완료
    }

}

인터페이스의 특징

1. 인터페이스는 다중 상속이 가능하다. (extends 는 동일한 메서드가 있을경우 이전에 설명한 충돌의 우려 때문에 다중상속 불가 -> ambiguous 에러)

2. 여러개의 인터페이스를 상속받은 인터페이스를 클래스에 적용할 땐 모든 메서드를 구현해 줘야한다.

3. 추상메서드와 상수로만 이뤄진다(코드의 종속성을 낮추기 위함, 접근제어자는 public이 붙으며 상수처럼 값이 명확하거나 메서드가 추상적이어야 타객체에서 접근할때 문제가 없다)

4. 생성자 사용 불가(객체가 아니기 때문이다)

5. 인터페이스의 조상은 인터페이스만 가능하다


인터페이스 구현

인터페이스에 구현된 추상메서드를 완성하는것으로 클래스 뒤쪽에 implements 인터페이스명을 붙인다

또한 기존 인터페이스선언에 있던걸 구현하는건 오버라이드기능을 활용하는 것이다.

//인터페이스 구현
//class 클래스명 implements 인터페이스 이름{..}

class Unit implements Play_Unit{
	public void move(int x, int y){
    	System.out.println("캐릭터가"+x+"위치에서"+y+"위치로 이동합니다");	//구현완료
    }
    
	public void attack(string m){
    	System.out.println("캐릭터가+m+"를 공격합니다.");	//구현완료
    }

}

 

 

여담..

기존의 인터페이스는 추상메서드만 가질수 있지만 디폴트 메서드가 생김으로써 메서드간 충돌할 우려가 생긴것인데 이것은 크게 인터페이스간 충돌, 디폴트메서드와 조상클래스 메서드간 충돌로 나뉘는데

인터페이스간 충돌 > 인터페이스 구현 클래스에서 디폴트 메서드 오버라이딩

조상클래스와 충돌 -> 조상클래스 메서드 상속, 디폴트메서드 무시됨

2가지의 이유로 사용하여도 문제가 없다고 한다.