Java 인터페이스(interface)

Jinny·2022년 3월 3일
0

TIL

목록 보기
28/28
post-thumbnail

추상메소드로만 구성된 추상클래스이다.
추상클래스와 다르게 일반 메소드와 멤버변수를 멤버로 가질 수 없고, 상수(public static final 필드)만 멤버로 가진다.

[접근제한자] interface 인터페이스이름 {
	public static final 타입 상수이름 =;  // 상수, 변수는 가질 수 없고 상수만 가능
	[public abstract] 반환자료형 메소드이름(매개변수목록); // 추상메소드
	// public abstract가 생략되기 때문에 오버라이딩 시 반드시 public 표기
}

인터페이스에 정의된 모든 멤버에 예외없이 적용되는 사항이기 때문에 제어자를 생략할 수 있다.

interface PlayingCare {
	// 상수
	public static final int SPADE = 4;
	final int DIAMOND = 3; // public static final int DIAMOND = 3;
	static int HEART = 2;  // public static final int HEART = 2;
	int CLOVER = 1;        // public static final int CLOVER = 1;
	
	// 추상메소드
	public abstract String getCardNumber();
	String getCardKind();  // public abstract String getCardKind();
}

특징

  • 모든 인터페이스의 메소드는 묵시적으로 public이고 abstract
  • 변수는 묵시적으로 public static final, 따라서 인터페이스의 변수의 값 변경 시도 시 에러 발생
  • 객체 생성은 안되나 참조형 변수로는 가능

장점(자바의정석)

  • 개발 시간을 단축시킬 수 있다.
  • 표준화가 가능하다.
  • 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.
    (두 객체 간의 연결, 대화, 소통을 돕는 중간 역할을 한다. ex) 사람 → 화면(인터페이스) → 컴퓨터)
  • 독립적인 프로그래밍이 가능하다.
  • 선언(설계)와 구현을 분리시킬 수 있게 한다.

장점(PPT)

  • 상위 타입 역할로 다형성을 지원하여 연결
  • 해당 객체가 다양한 기능 제공 시에도 인터페이스에 해당하는 기능만을 사용하게 제한 가능
  • 공통 기능 상의 일관성 제공
  • 공동 작업을 위한 인터페이스 제공

추상클래스와 인터페이스

인터페이스의 상속

인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속이 가능하다.

💡 public interface 인터페이스 extends 인터페이스1, 인터페이스2, ... { // 상수 또는 추상 메소드 }

추상메소드에 대한 구현(implement)과 재정의(Overriding)는 인터페이스를 상속받은 후손 클래스에서 구현해야 한다.

인터페이스의 구현

인터페이스가 가진 추상 메소드를 구현하려면 implements 키워드를 사용하여 자식 클래스가 인터페이스를 상속받아야 한다.
상속받은 자식 클래스는 반드시 추상메소드를 구현해야 한다.

class 클래스 이름 implements 인터페이스이름 {
	// 인터페이스에 정의된 추성메소드를 구현해야 한다.
}

class Fighter implements Fightable {
	public void move(int x, int y) { ... }
	public void attack(Unit u) { ... }
}

만일 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야 한다.

abstract class Fighter implements Fightable {
	public void move(int x, int y) { ... }
}

상속과 구현을 동시에 할 수 있다.

class Fighter extends Unit implements Fightable {
	// 추상클래스 완성과 동일(몸통{} 만들기)
	public void move(int x, int y) { ... }
	public void attack(Unit u) { ... }
}

예제

package com.inter.controller;

import com.inter.common.PrintDriver;

public class PrinterController {
	private PrintDriver print; // 인터페이스등록 참조형으로 등록

	public PrinterController(PrintDriver print) {
		super();
		this.print = print;
	}
	
	public void print() {
		// 연결된 프린터기를 이용한 인쇄하는 기능
		// print.printHp();
		// print.lgPrint();
		print.print();
		print.test();
	}
}

인터페이스

package com.inter.common;

import java.io.Serializable;

// 인터페이스 상속은 다중상속이 가능!
public interface PrintDriver extends Serializable, Runnable {
	void print();
	void scan();
	
	// 예외적인 상황에서 추가메소드를 구현하는 방식
	default public void test() {
		System.out.println("안녕하세요.");
	}
}

인터페이스 구현객체

package com.inter.model.vo;

import com.inter.common.PrintDriver;

public class HpPrinter implements PrintDriver {
	/**
	 * 
	 */
	private static final long serialVersionUID = -2821671434538341460L;

	public void printHp() {
		System.out.println("HP프린터 인쇄중!");
	}
	
	@Override
	public void print() {
		printHp();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scan() {
		// TODO Auto-generated method stub
		
	}
}

인터페이스

package com.inter.model.vo;

import com.inter.common.PrintDriver;

public class SamsungPrinter implements PrintDriver {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3572053846093501093L;

	public void print() {
		System.out.println("삼성프린트 인쇄중..");
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void scan() {
		// TODO Auto-generated method stub
		
	}
}

실행

package com.inter.run;

import com.inter.controller.PrinterController;
import com.inter.model.vo.HpPrinter;
import com.inter.model.vo.SamsungPrinter;

public class Main {
	public static void main(String[] args) {
		new PrinterController(new SamsungPrinter()).print();
		new PrinterController(new HpPrinter()).print();
	}
}

// 실행 결과
삼성프린트 인쇄중..
안녕하세요.
HP프린터 인쇄중!
안녕하세요.

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

인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.

// 인터페이스 Fightable를 Fighter가 구현했을 때
Fightable f = (Fightable)new Fighter();
// 또는
Fightable f = new Fighter();

인터페이스는 메소드의 매개변수 타입으로 사용될 수 있다.
메소드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야 한다.

class Fighter extends Unit implements Fightable {
	public void move(int x, int y) { ... }
	public void attack(Fightable f) { ... }
}
// 대신 interface가 쓸 수 있는 멤버변수는 interface 클래스가 갖고 있는 추상메소드의 개수만 사용가능

메소드의 리턴타입으로 인터페이스의 타입을 지정하는 것도 가능하다.

Fightable /*<- 리턴타입*/ method() { 
	...
	Fighter f = new Fighter();
	return f; // Fighter 객체 반환
	// return new Fighter(); 으로도 작성 가능
} 

리턴 타입이 인터페이스라는 것은 메소드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환하는 것을 의미한다.

디폴트 메소드와 static 메소드

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

디폴트 메소드

추상 메소드의 기본적인 구현을 제공하는 메소드로, 추상메소드가 아니기 때문에 디폴트 메소드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

interface MyInterface {
	void method();
	default void newMethod() {} // 몸통을 구현해도 interface가 유지된다.
}

기존 메소드와 이름이 중복되어 충돌하는 경우가 많은데 해결하려면

  1. 여러 인터페이스의 디폴트 메소드 간의 충돌
    • 인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩 해야된다.
  2. 디폴트 메소드와 부모 클래스의 메소드 간의 충돌
    • 부모 클래스의 메소드가 상속되고, 디폴트 메소드는 무시된다.

Q & A

Q : 인터페이스란 ?

A : 추상메소드의 집합

Q : 인터페이스의 구현이란 ?

A : 인터페이스의 추상메소드 몸통{} 만들기 (미완성 설계도 완성하기)

Q : 추상클래스와 인터페이스의 공통점은?

A : 추상 메소드를 가지고 있다. (미완성 설계도)

Q : 추상클래스와 인터페이스의 차이점은?

A : 인터페이스는 멤버변수를 가질 수 없다. (상수, 추상메소드만 가능)


인터페이스 실습문제



Drink

package com.kh.prac7.model.vo;

public class Drink extends Menu {

	public int size; // 마실 것 사이즈(ml)

	public Drink() {
		super();
	}

	public Drink(int size) {
		super();
		this.size = size;
	}

	public Drink(String name, int price, int size) {
		super(name, price);
		this.size = size;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	@Override
	public void sell(int orderQuantity) {
		System.out.println("마실 것 : " + super.getName() 
						+ ", 사이즈(ml) : " + size 
						+ ", 주문 개수 : " + orderQuantity 
						+ ", 주문 가격 : " + (orderQuantity * super.getPrice()));

	}

}

Menu(추상클래스)

package com.kh.prac7.model.vo;

public abstract class Menu implements Restaurant {
	
	private String name; // 음식명
	private int price; // 가격
	public Menu() {
		super();
	}
	public Menu(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
}

Meat

package com.kh.prac7.model.vo;

public class Meat extends Menu {
	
	private int perGram; // 1인분 그램 수

	public Meat() {
		super();
	}

	public Meat(int perGram) {
		super();
		this.perGram = perGram;
	}

	public Meat(String name, int price, int perGram) {
		super(name, price);
		this.perGram = perGram;
	}

	public int getPerGram() {
		return perGram;
	}

	public void setPenGram(int perGram) {
		this.perGram = perGram;
	}

	@Override
	public void sell(int orderQuantity) {
		System.out.println("고기 : " + super.getName() 
						+ ", 1인분 무게(g) : " + perGram 
						+ ", 주문량 : " + orderQuantity 
						+ ", 주문 가격 : " + (orderQuantity * super.getPrice()));
		
	}
}

Restaurant(인터페이스)

package com.kh.prac7.model.vo;

public interface Restaurant {
	
	/*public abstract*/ void sell(int orderQuantity);
	
}

Run

package com.kh.prac7.run;

import com.kh.prac7.model.vo.Drink;
import com.kh.prac7.model.vo.Meat;
import com.kh.prac7.model.vo.Menu;

public class Run {
	
	public static void main(String[] args) {
		
		Menu[] m = new Menu[3];
		
		m[0] = new Meat("와인 삼겹", 6000, 160);
		m[1] = new Drink("코카콜라", 1000, 500);
		m[2] = new Drink("참이슬", 4000, 360);
		
		// 장사하기
		m[0].sell(3);
		m[2].sell(1);
		m[0].sell(2);
		m[1].sell(2);
		
	}
	
}

실행 결과

profile
코린이

0개의 댓글