Java 인터페이스

I C-AN·2021년 7월 29일
0

Java

목록 보기
16/26
post-thumbnail

인터페이스

추상 메서드의 집합으로 그 외의 상수, static 메서드, default 메서드로 구성할 수 있다
구현된 것이 전혀 없는 설계도로 모든 멤버가 public이다

인터페이스의 구성

interface PlayingCard {
	// 상수
	public static final int SPADE = 4;
	final int DIAMOND = 3;
	static int HEART = 2;
	int CLOVER = 1; // 항상 예외없이 public static final이 붙으며 생략 가능
	// 추상 메서드
	public abstract String getCardNumber();
	String getCardKind(); // public abstract가 생략되어 있음
}

인터페이스와 추상 클래스

추상 클래스 : 추상 메서드를 가진 일반 클래스
인터페이스 : 추상 메서드의 집합으로 다중 상속이 가능하며 iv를 가질 수 없다

추상 클래스는 상속을 통해 추상 메서드를 강제적으로 구현하는 것이 목적이라면
인터페이스는 상속이 아닌 규칙을 정함으로써 동일한 동작을 구현하는 것이 차이이다

인터페이스의 상속

interface Movable { void move(int x, int y); }

interface Attackable { void attack(Unit u); }

interface Fightable extends Movable, Attackable {} // 다중 상속, 멤버 2개
  • 인터페이스의 부모는 인터페이스만 가능 (Object가 최고 조상이 아님)
  • 다중 상속이 가능 (추상 메서드는 구현부가 없어 충돌해도 문제 없음)

인터페이스의 구현

인터페이스에 정의된 추상 메서드를 완성하는 것

class Fighter implements Fightable {
	public void move(int x, int y) {}
	public void attack(Unit u) {}
}
  • implements 키워드로 인터페이스를 구현한다
  • 해당 키워드가 붙은 클래스는 인터페이스의 추상 메서드를 모두 구현(재정의)해야 한다
  • 일부만 구현하는 경우 abstract 키워드를 붙여 추상 클래스임을 명시해야 한다

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

// 상속을 받으며 구현이 가능 (다중 상속)
class Fighter extends Unit implements Fightable {
	public void move(int x, int y) {}
    
	// 인터페이스 타입의 매개변수
	public void attack(Fightable f) {}
    
	// 인터페이스 리턴타입
	Fightable getFightable() {
		Fighter f = new Fighter();
		return f;
}

// 인터페이스 타입의 참조변수
Unit u = new Fighter(); // Unit이 부모 객체라서 가능
Fightable f = (Fightable) new Fighter();
Fightable f2 = new Fighter();
  • 인터페이스는 구현 클래스의 부모객체이다
  • 인터페이스 또한 타입으로 취급한다

인터페이스 타입의 참조변수

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

인터페이스 타입의 매개변수

public void attack(Fightable f) {}
  • 메서드가 인터페이스 타입의 매개변수를 가질 수 있다
  • 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야 한다

인터페이스 리턴타입

Fightable getFightable() {
	Fighter f = new Fighter();
	return f;
}
  • 메서드의 리턴타입을 인터페이스 타입으로 지정할 수 있다
  • 해당 인터페이스를 구현한 클래스의 인스턴스를 반환해야 한다

인터페이스 다형성 예제

interface Parseable {
	// 구문 분석작업을 수행한다
	public abstract void parse(String fileName);
}

class ParserManager {
	// 인터페이스 리턴타입
	public static Parseable getParser(String type) {
		if(type.equals("XML")) {
			return new XMLParser();
		} else {
			Parseable p = new HTMLParser();
			return p;
			// return new HTMLParser();
		}
	}
}

// 인터페이스 구현
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");
	}
}

인터페이스의 장점

  • 개발 시간을 단축시킬 수 있다
  • 일관되고 정형화된 개발을 위한 표준화가 가능하다
  • 서로 관계없는 클래스를 묶을 수 있다
  • 클래스마다 독립적인 프로그래밍이 가능하다
class A {
	public void method(B b) { // B 객체만 받을 수 있다
		b.method();
	}
}

class A2 {
	public void method(I i) { // I를 구현한 객체를 받을 수 있다
		i.method();
	}
}

interface I {
	abstract public void method();
}

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

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


A a = new A();
a.method(new B());
// a.method(new C()); // C 객체를 받으려면 A 클래스를 수정해야 함

A2 a2 = new A2();
a2.method(new B());
a2.method(new C()); // C 객체 또한 I를 구현했기 때문에 수정할 필요가 없음

default 메서드와 static 메서드

JDK1.8부터는 default 메서드와 static 메서드를 인터페이스에 작성할 수 있게 되었다. static 메서드는 인스턴스를 생성하지 않아도 사용할 수 있는 독립적인 메서드지만 default 메서드의 경우는 다르다.

인터페이스에 메서드를 추가한다는 것은 추상 메서드를 추가한다는 것이고, 해당 인터페이스를 구현하는 모든 클래스가 새로운 메서드를 구현하는 현상이 생기게 된다. default 메서드는 이러한 문제를 해결하기 위해 추가된 방법으로 추상 메서드의 기본적인 구현을 제공하는 메서드라 추상 메서드가 아니기 때문에 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

default 메서드와 static 메서드 예제

  
class DefaultMethodTest {
	public static void main(String[] args) {
		Child c = new Child();
		c.method1();
		c.method2();
		MyInterface.staticMethod(); 
		MyInterface2.staticMethod();
	}
}

class Child extends Parent implements MyInterface, MyInterface2 {
	public void method1() {	
		System.out.println("method1() in Child");
	}			
}

class Parent {
	public void method2() {	
		System.out.println("method2() in Parent");
	}
}

interface MyInterface {
	default void method1() { 
		System.out.println("method1() in MyInterface");
	}
	
	default void method2() { 
		System.out.println("method2() in MyInterface");
	}

	static  void staticMethod() { 
		System.out.println("staticMethod() in MyInterface");
	}
}

interface MyInterface2 {
	default void method1() { 
		System.out.println("method1() in MyInterface2");
	}

	static  void staticMethod() { 
		System.out.println("staticMethod() in MyInterface2");
	}
}
method1() in Child
method2() in Parent
staticMethod() in MyInterface
staticMethod() in MyInterface2
profile
할 수 있다

0개의 댓글