[JAVA] 인터페이스

안요한·2022년 5월 19일
0

JAVA

목록 보기
12/16

7.1 인터페이스란?

  • 일종의 추상클래스.
  • 추상메서드를 갖지만, 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다.
  • 오직 추상메서드와 상수만을 멤버로 가질 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않는다.
  • 다른 클래스를 작성하는데 도움 줄 목적으로 작성된

7.2 인터페이스의 작성

interface 인터페이스이름 {
		public static final 타입 상수이름 =;
		public abstract 메서드이름(매개변수목록);
}
  • 인터페이스의 제약사항
  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
  • 단, static메서드와 디폴드 메서드는 예외(JDK1.8부터)

7.3 인터페이스의 상속

  • 인터페이스는 인터페이스로부터만 상속 가능
  • 클래스와는 달리 다중상속, 즉 여러 개의 인터페이스로부터 상속을 받는 것이 가능.
interface Movable {
		/** 지정된 위치(x, y)로 이동하는 기능의 메서드 **/
		void move(int x, int y);
}
interface Attackable {
		/** 지정된 대상(u)을 공격하는 기능의 메서드 **/
		void attack(Unit u);
}

interface Fightable extends **Moveable**, **Attackable**{}
// 자손 인터페이스(Fightable)는 조상 인터페이스(Movable, Attackable)에 정의된 멤버 모두 상속
// 그래서 Fightable에는 정의된 멤버가 없지만, 상속받은 move,attack을 멤버로 갖게 된다.

7.4 인터페이스의 구현

  • 인터페이스도 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 하는데, 키워드 'implements'를 사용.

  • 예제 7-24/FighterTest.java

class FighterTest {
	public static void main(String[] args) {
		Fighter f = new Fighter();

		if (f instanceof Unit)	{		
			System.out.println("f는 Unit클래스의 자손입니다.");
		}
		if (f instanceof Fightable) {	
			System.out.println("f는 Fightable인터페이스를 구현했습니다.");
		}
		if (f instanceof Movable) {		
			System.out.println("f는 Movable인터페이스를 구현했습니다.");
		}
		if (f instanceof Attackable) {	
			System.out.println("f는 Attackable인터페이스를 구현했습니다.");
		}
		if (f instanceof Object) {		
			System.out.println("f는 Object클래스의 자손입니다. ");
		}
	}
}

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

class Unit {
	int currentHP;	
	int x;			
	int y;			
}

interface Fightable extends Movable, Attackable { }
interface Movable    {	**void move(int x, int y);**	}
interface Attackable {	void attack(Unit u); 		}
  • Movable 인터페이스에 정의된 'void move(int x, int y)'를 Fighter클래스에서 구현할 때 접근 제어자를 public으로 했다는 것이다.
  • 오버라이딩 할 때는 조상의 메서드보다 넓은 범위의 접근 제어자를 지정해야 한다.
  • void move(int x, int y)는 'public abstract'가 생략된 것이다.
  • 그래서 이를 구현하는 Fighter클래스에서는 반드시 public으로 해야하는 것이다.

7.5 인터페이스를 이용한 다중상속

만일 두 개의 클래스로부터 상속을 받아야 할 상황이라면, 두 조상클래스 중에서 비중이 높은 쪽을 선택하고 다른 한쪽은 클래스 내부의 멤버로 포함시키는 방식으로 처리하거나 어느 한쪽의 필요한 부분을 뽑아서 인터페이스로 만든 다음 구현하도록 한다.

public class Tv {
		protected boolean power;
		protected int channel;
		protected int volume;

		public void power()       { power =! power; }
		public void channelUp()   { channel++; }
		public void channelDown() { channel--; }
		public void volumeUp()    { volume++; }
		public void volumeDown()  { volume--; }
}

public class VCR {
		protected int counter; // VCR의 카운터
		
		public void play() {
					// Tape을 재생한다.
		}
		public void stop() {
					// Tape을 멈춘다.
		}
		public void reset() {
					counter = 0;
		}
		public int getCounter() {
					return counter;
		}
		public void setCounter(int c) {
					counter = c;
		}
}

/* VCR클래스에 정의된 메서드와 일치하는 추상메서드를 갖는 인터페이스를 작성한다. */

		public interface IVCR {
					public void play();
					public void stop();
					public void reset();
					public int  getCounter();
					public void setCounter(int c);
		}

/* 이제 IVCR 인터페이스를 구현하고 Tv클래스로부터 상속받는 TVCR클래스를 작성한다.
		이때 VCR클래스 타입의 참조변수를 멤버변수로 선언하여
		 IVCR인터페이스의 추상메서드를 구현하는데 사용한다. */

public class TVCR extends Tv implements IVCR {
		VCR vcr = new VCR();

		public void play() {
					vcr.play();  //코드를 작성하는 대신 VCR인스턴스의 메서드를 호출한다.
		}
		public void stop() {
					vcr.stop();
		}
		public void reset() {
					vcr.reset();
		}
		public int  getCounter() {
					return vcr.getCounter();
		}
		public void setCounter(int c) {
					vcr.setCounter(c);
		}

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

  • 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
Fightable f = (Fightable)new Fighter();

또는

Fightable f = new Fighter();

//따라서 인터페이스는 메서드의 매개변수 타입으로 사용 가능
Fightable method() {
	...
	Fighter f = new Fighter();
	return f;
}
//리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 
//클래스의 인스턴스를 반환한다는 것을 의미한다.
  • 예제 7-25/ParserTest.java
interface Parseable {
	//구문 분석작업을 수행한다.
	public abstract void parse(String fileName);
}
/*구문분석을 수행하는 기능을 구현할 목적으로 추상메서드 'parse(String fileName)'을 정의했다.*/

class ParserManager {
	//리턴타입이 Parseable인터페이스이다.
	public static Parseable getParser(String type) {
		if(type.equals("XML")) {
			return new XMLParser();
		} else {
			Parseable p = new HTMLParser();
			return p;
			// return new HTMLParser();
		}
	}
}
/* getParser메서드는 매개변수로 넘겨받는 type의 값에 따라 XMLParse인스턴스 
또는 HTMLParser인스턴스를 반환한다.*/
/* getParser메서드의 수행결과로 참조변수 parser는 XMLParser인스턴스의 주소값을 갖게 된다
마치 'Parseable parser = new XMLParser();'이 수행된 것과 같다.

class XMLParser implements Parseable {
	public void parse(String fileName) {
		/* 구문 분석작업을 수행하는 코드를 적는다.*/
		System.out.println(fileName + "- XML parsing completed.");
	}
}

class HTMLParser implements Parseable {
	public void parse(String fileName) {
		/* 구문 분석작업을 수행하는 코드를 적는다.*/
		System.out.println(fileName + "-HTML parsing completed.");
	}
}

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

/* getParser메서드의 수행결과로 참조변수 parser는 

7.7 인터페이스의 장점

  1. 개발시간을 단축시킬 수 있다.

→ 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다.

메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다.

  1. 표준화가 가능하다.

→ 기본틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하도록 함으로써 일관되고 정형화된 프로그램의 개발이 가능.

  1. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.

→ 아무 관계없는 클래스들에게 하나의 인터페이스를 공통적으로 구현함으로써 관계를 맺어 줄 수 있다.

  1. 독립적인 프로그래밍이 가능하다.

→ 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다.

클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해 간접적인 관계로 변경하면, 한 클래스의 변경이 다른 클래스에 영향을 미치지 않는 프로그래밍이 가능하다.

  • 예제 7-26/RepairableTest.java
class RepairableTest{
	public static void main(String[] args) {
		Tank tank = new Tank();
		Dropship dropship = new Dropship();

		Marine marine = new Marine();
		SCV	scv = new SCV();

		scv.repair(tank);	//SCV가 Tank를 수리하도록 한다.
		scv.repair(dropship);
//	scv.repair(marine);	//매린은 GroundUtnit이지만, Repairable인터페이스를 작성하지 않아서 수리 불가
	}
}

interface Repairable {}
class GroundUnit extends Unit2 {
	GroundUnit(int hp) {
		super(hp);
	}
}

class AirUnit extends Unit2 {
	AirUnit(int hp) {
		super(hp);
	}
}

class Unit2 {
	int hitPoint;
	final int MAX_HP;
	Unit2(int hp) {
		MAX_HP = hp;
	}
	//...
}

class Tank extends GroundUnit implements Repairable {
	Tank() {
		super(150);		//탕크의 피는 150
		hitPoint = MAX_HP;
	}

	public String toString() {
		return "Tank";
	}
	//...
}

class Dropship extends AirUnit implements Repairable {
	Dropship() {
		super(125);		//돱십 피는 125
		hitPoint = MAX_HP;
	}

	public String toString() {
		return "Dropship";
	}
	//...
}

class Marine extends GroundUnit {
	Marine() {
		super(40);
		hitPoint = MAX_HP;
	}
	//...
}

class SCV extends GroundUnit implements Repairable{
	SCV() {
		super(60);
		hitPoint = MAX_HP;
	}

	/*repair메서드의 매개변수 r은 Repariable타입이기 때문에 인터페이스 Reapairable에 정의된 
	멤버만 사용할 수 있다. 그래서 instanceof연산자로 타입을 체크한 뒤 캐스팅하여 Unit클래스에 정의된
	hitPoint와 MAX_HP를 사용할 수 있도록 하였다.*/
	void repair(Repairable r) {
		if (r instanceof Unit2) {
			Unit2 u = (Unit2)r;
			while(u.hitPoint!=u.MAX_HP) {
				//유닛의 피를 채움
				u.hitPoint++;
			}
			System.out.println( u.toString() + "의 수리가 끝났습니다.");
		}
	}	
	//...
}

7.8 인터페이스의 이해

인터페이스의 이해를 위해서는 아래 두 가지 사항을 반드시 염두해야함.

클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.

메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다.(내용은 몰라도 됨)

  • 예제 7-29/InterfaceTest3.java
class InterfaceTest {
	public static void main(String[] args) {
		A a = new A();
		a.methodA();
	}
}

 class A {
    void methodA() {
      I i = InstanceManager.getInstance();
		  i.methodB();
		  System.out.println(i.toString()); //i로 Object클래스의 메서드 호출가능
     }
 }

 interface I {
      public abstract void methodB();
 }

 class B implements I {
     public void methodB() {
          System.out.println("methodB in B class");
     }

	 public String toString() { return "class B";}
 }

 class InstanceManager {
	public static I getInstance() {
		return new B(); //다른 인스턴스로 바꾸려면 여기만 변경하면 됨.
	}
 }

7.9 디폴트 메서드와 static메서드

원래는 인터페스에 추상 메서드만 선언할 수 있는데 , JDK1.8부터 디폴트 메서드와 static메서드도 추가할 수 있게 되었다.

디폴트 메서드

  • 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상 메서드가 아니기 때문에 디폴트 메서드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
  • 디폴트 메서드는 앞에 키워드 default를 붙이며, 추상 메서드와 달리 몸통{}이 있어야 한다. public이며, 생략 가능.
  • 새로 추가된 디폴트가 기존 메서드의 이름과 중복 되어 충돌하는 경우 해결법
    1. 여러 인터페이스의 디폴트 메서드간의 충돌

      → 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.

    2. 디폴트 메서드와 조상 클래스의 메서드간의 충돌

      → 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.

profile
걍이렇게돼브렀다리

0개의 댓글