[Java] 인터페이스와 추상 클래스

정보구니·2022년 1월 4일
1

Java

목록 보기
35/39
post-thumbnail

인터페이스


인터페이스란 기능을 사용하는 방법을 명시하는 것으로,
자바의 다형성을 극대화하여 개발코드 수정을 줄이고 프로그램 유지 보수성을 높이기 위해 인터페이스를 사용한다.



🔎 인터페이스 정의하기

  • 인터페이스는 interface 키워드를 통해 선언 할 수 있다.
  • 인터페이스의 메소드는 몸체가 없이 세미콜론으로 마무리 되며, 이러한 메소드를 추상 메소드라 한다.

interface Printable {
	public void print(String doc);  // 추상 메소드
}



🔎 인터페이스를 구현하는 클래스

  • implements 키워드를 통해 일반 클래스에서 인터페이스를 구현할 수 있다.
  • 한 클래스는 둘 이상의 인터페이스를 동시에 구현할 수 있다.
  • 상속과 구현은 동시에 가능하다.

class Printer implements Printalble {
	public void print(String doc) {  // Printable 인터페이스의 print 메소드 구현
    	system.out.println(doc);
    }
}



🔎 인터페이스 특징

  • 인터페이스 대상으로는 인스턴스 생성이 불가능하다.

  • 인터페이스형 참조변수 선언이 가능하다.
    인터페이스를 직접 혹은 간접적으로 구현하는 모든 클래스의 인스턴스를 참조할 수 있다.

  • 구현하는 메소드와 추상 메소드 사이에도 메소드 오버라이딩 관계가 성립한다.
    ➡ 어노테이션 @Override 선언이 가능

interface Printable {
	public void print(String doc); // 추상 메소드
}

class Printer implements Printable { // Printable을 구현하는 Printer 클래스
	@Override
	public void print(String doc) { // 오버라이딩 관계 성립
		System.out.println(doc);
	}
}

class InterfaceExam {
	public static void main(String[] args) {
		Printable p = new Printer(); // 인터페이스(Printable)형 참조변수 선언 가능
		p.print("Hello Java"); // 오버라이딩 한 메소드가 호출됨
	}
}


// 출력 결과
Hello Java




🔎 관련 예제 (프린터드라이버)

마이크로소프트의 윈도우에서 삼성과 LG의 프린터를 대상으로 출력이 가능할 때, 마이크로소프트에서 인터페이스를 만들어 모든 프린터 업체에게 제공을 한다.

프린터 업체들은 제공된 인터페이스를 가지고 자사의 프린터를 조작하는 메소드를 구현하여 클래스를 정의하고 제공한다.

그럼 마이크로소프트는 클래스 이름만 알면 내부적으로 구현이 어떻게 이뤄지는지 몰라도 해당 업체의 프린터로 출력할 수 있다.

이것이 바로 인터페이스를 두는 이유이다 !

interface Printable2 {  // MS가 정의하고 제공한 인터페이스
	public void print(String doc); // 추상 메소드
}

class LPrinterDriver implements Printable { // L사의 프린터 사용에 필요한 클래스 정의
	@Override
	public void print(String doc) {
		System.out.println("Form LG printer");
		System.out.println(doc);
	}
}

class SPrinterDriver implements Printable { // S사의 프린터 사용에 필요한 클래스 정의
	@Override
	public void print(String doc) {
		System.out.println("From Samsung printer");
		System.out.println(doc);
	}
}

class PrinterDriver {
	public static void main(String[] args) {
		String myDoc = "This is a report about...";
		
		// 삼성 프린터로 출력
		Printable p = new SPrinterDriver();
		p.print(myDoc);
		System.out.println(); // 단순 개행 
		
		// LG 프린터로 출력
		p = new LPrinterDriver();
		p.print(myDoc);
	}
}


// 출력 결과
From Samsung printer
This is a report about...

Form LG printer
This is a report about...









인터페이스의 문법 구성


  • 인터페이스의 메소드에는 추상 메소드, 디폴트 메소드, static 메소드가 있다.
  • 인터페이스 간 상속이 가능하다.
  • 인터페이스 형(type) 이름을 대상으로 instanceof 연산을 할 수 있다.




인터페이스에 선언되는 메소드와 변수

🔎 인터페이스의 메소드

  • 인터페이스의 모든 추상 메소드는 public이 선언된 것으로 간주한다. ( 생략 가능 )



🔎 인터페이스의 변수

  • 반드시 선언과 동시에 값으로 초기화를 해야한다.
  • 모든 변수는 public, static, final이 선언된 것으로 간주한다. ( 생략 가능 )
    ➡ 즉, 인터페이스 내에 선언된 변수는 상수 !
  • 인터페이스 내에 위치한 변수는 상수이므로 이름을 모두 대문자로 작성한다.



🔎 인터페이스를 구현하는 클래스

  • 인터페이스를 구현하는 클래스는 인터페이스에 존재하는 모든 추상 메소드를 구현해야 한다.
    ➡ 구현하지 않으면, 해당 클래스를 대상으로 인스턴스 생성이 불가능 !





인터페이스 간 상속

인터페이스에 새로운 기능을 위한 추상메소드가 추가될 경우(예를 들어 위 예제에서 컬러 프린터가 생기는 경우),
인터페이스의 모든 추상 메소드는 이를 구현하는 클래스에서 모두 구현해야 하므로 기존에 개발된 드라이버를 모두 수정해야할 것이다.

이러한 상황을 고려해 자바에서는 인터페이스의 상속을 지원한다.


🔎 인터페이스 상속 방법

  • 두 인터페이스 사이의 상속도 extends로 명시한다.
  • 인터페이스와 클래스 사이의 구현만 implements로 명시한다.

interface Printable { // MS사가 제공한 인터페이스
	void print(String doc); // 흑백 출력을 위한 추상 메소드
}

/* Printable를 상속하는 인터페이스 */
interface ColorPrintable extends Printable {
	void printColor(String doc); // 컬러 출력을 위한 추상 메소드
}

// L사의 컬러 프린터 드라이버
class LPrinterColorDriver implements ColorPrintable {
	@Override
	public void print(String doc) { // 흑백 출력 메소드
		System.out.println("From LG black & white print");
		System.out.println(doc);
	}
	@Override
	public void printColor(String doc) { // 컬러 출력 메소드
		System.out.println("From LG color print");
		System.out.println(doc);
	}
}

public class PrinterDriver {
	public static void main(String[] args) {
		String myDoc = "This is a report about...";
		
		ColorPrintable p = new LPrinterColorDriver();
		p.print(myDoc); // 흑백 출력
		System.out.println(); // 단순 개행
		p.printColor(myDoc); // 컬러 출력
	}
}


// 출력 결과
From black & white print
This is a report about...

From color print
This is a report about...





인터페이스의 디폴트 메소드

디폴트 메소드는 인터페이스에 추상 메소드를 추가해야 하는 상황에서 이전에 개발해 놓은 코드에 영향을 미치지 않기 위해 등장한 문법이다.

기능 보강을 위해 모든 인터페이스에 최소 한개 이상의 추상 메소드를 추가해야 하는 경우, 인터페이스의 상속으로 해결한다면 인터페이스의 수는 두배로 늘어나게 될 것이다.

이렇게 인터페이스의 수가 늘어가는 것은 프로그램 개발에 불편을 초래하는 일이다.

이러한 상황의 해결을 위해 인터페이스의 디폴트 메소드를 지원한다.




🔎 디폴드 메소드의 선언과 특징

  • default 키워드를 통해 선언 할 수 있다.
  • 자체로 완전한 메소드이다. (몸체가 있음)
  • 따라서 이를 구현하는 클래스가 오버라이딩 하지 않아도 된다.

interface Printable {
	void print(String doc);
	default void printColor(String doc) {} // 인터페이스의 디폴트 메소드
}

class LPrinterDrv implements Printable { // L사 프린터 드라이브 (흑백 출력만 가
	@Override
	public void print(String doc) {
		System.out.println("From LG black & white printer");
		System.out.println(doc);
	}
}

class SPrinterDrv implements Printable { // S사 프린터 드라이브 (흑백, 컬러 출력 모두 가능)
	@Override
	public void print(String doc) {
		System.out.println("From Samsung black & white printer");
		System.out.println(doc);
	}
	
	@Override
	public void printColor(String doc) {
		System.out.println("From Samsung color printer");
		System.out.println(doc);
	}
} 

class PrinterDriver {
	public static void main(String[] args) {
		String myDoc = "This is a report about...";
		
		Printable p1 = new LPrinterDrv();
		p1.print(myDoc);
		System.out.println();
		
		Printable p2 = new SPrinterDrv();
		p2.print(myDoc);
		System.out.println();
		p2.printColor(myDoc);
	}
}


// 출력 결과
From LG black & white printer
This is a report about...

From Samsung black & white printer
This is a report about...

From Samsung color printer
This is a report about...





인터페이스의 static 메소드 (클래스 메소드)

  • 인터페이스에도 static 메소드를 사용할 수 있다.

interface Printable {
	static void printLine(String str) { // static 메소드
		System.out.println(str);
	}
	
	default void print(String doc) {
		printLine(doc); // 인터페이스의 static 메소드 호출
	}
}

// 인터페이스 Printale4에는 구현해야 할 메소드가 존재 하지 않음
class Printer implements Printable { }

class InterfaceStaticExam {
	public static void main(String[] args) {
		String myDoc = "This is a report about...";
        
		Printable p = new Printer();
		p.print(myDoc);
		
		Printable4.printLine("end fo line"); // 인터페이스의 static 메소드 직접 호출
	}
}


// 결과 출력
This is a report about...
end fo line





인터페이스 대상의 instanceof 연산

  • 참조변수가 참조하는 인스턴스가 해당 클래스를 직접 또는 간접적으로 구현하는 클래스의 인스턴스일 경우 true를 반환한다.

interface Printable {
	void printLine(String str);
}

class SimplePrinter implements Printable { // Printable를 직접 구현
	@Override
	public void printLine(String str) {
		System.out.println(str);
	}
}

class MultiPrinter extends SimplePrinter { // SimplePrinter를 상속함으로써 Printable4를 간접 구현함
	@Override
	public void printLine(String str) {
		super.printLine("start of multi...");
		super.printLine(str);
		super.printLine("end of multi...");
	}
}

public class InterfaceInstanceofExam {
	public static void main(String[] args) {
		Printable p1 = new SimplePrinter();
		Printable p2 = new MultiPrinter();
		
		if (p1 instanceof Printable)
			p1.printLine("This is a simple printer");
		System.out.println();
		
		if (p2 instanceof Printable)
			p2.printLine("This is a muliful printer");
	}
}


// 출력 결과
This is a simple printer

start of multi...
This is a muliful printer
end of multi...





Marker 인터페이스

  • 클래스에 특별한 표시을 해두기 위한 목적으로 정의 된 인터페이스를 마커 인터페이스라 한다.
  • 마커 인터페이스에는 아무런 메소드도 존재하지 않는 경우가 많다.

interface Upper{} // 마커 인터페이스
interface Lower{} // 마커 인터페이스

interface Printable {
	String getContents();
}

class Report implements Printable, Upper {
	String cons;
	
	
	Report(String cons) { // 생성자
		this.cons = cons;
	}
	
	@Override
	public String getContents() {
		return cons;
	}
}

class Printer {
	public void printContents(Printable doc) {
		if(doc instanceof Upper) // doc 참조인스턴스가 Upper를 구현한다면
			System.out.println((doc.getContents().toUpperCase())); // 문자열의 모든 문자를 대문자로 바꿈
		else if(doc instanceof Lower) // doc 참조인스턴스가 Lower를 구현한다면
			System.out.println((doc.getContents().toLowerCase())); // 문자열의 모든 문자를 소문자로 바꿈
		else
			System.out.println(doc.getContents());
	}
}

class MarkerInterfaceExam {
	public static void main(String[] args) {
		Printer p = new Printer();
		Report doc = new Report("Simple Funny News~");
		p.printContents(doc);
	}
}


// 출력 결과
SIMPLE FUNNY NEWS~  // doc 참조 인스턴스가 Upper를 구현하므로 대문자로 출력됨









추상 클래스 (Abstract class)


  • 하나 이상의 추상 메소드를 지니는 클래스를 가리켜 추상 클래스라 한다.
  • 클래스 선언부에 abstract 선언을 추가해 추상클래스를 정의한다.
  • 다른 클래스에 의해 추상 메소드가 구현되어야 한다.
  • 인스턴스 변수와 인스턴스 메소드를 갖지만, 이를 상속하는 하위 클래스에 의해 구현되어야 할 메소드가 하나 이상 있는 경우 추상 클래스라 한다.

public abstract class House {
	public void methodOne() {
    	System.out.println("method one");
    }
    
    	public abstract void methodTwo(); // 추상 메소드
}



🔎 추상 클래스의 특징

  • 추상 클래스를 대상으로는 인스턴스 생성이 불가능 하다.
  • 추상 클래스 형(type) 참조변수 선언은 가능하다.









참고
열혈 자바 ch17 (인터페이스와 추상 클래스)
자바 인터페이스란?

0개의 댓글