본문 바로가기

Java/문법

[Java] 인터페이스 - 개념, 일부 구현, 다중 구현, default & static 메서드

개념

왜 써야 돼?

💡 다중 구현을 통한 자유로운 상속 관계 ⇒ 클래스의 다형성보다 다채롭고 자유롭게 사용 가능

  • 프로그램을 설계하고 조금 더 유연한 프로그램을 만들기 위함
  • 추상화, 상속과 더불어 다형성이라는 객체 지향(OOP)의 특징을 구현하는 핵심
    • 인터페이스의 다형성
      • 부모클래스 타입으로 자식클래스 타입을 포함시킬 수 있음(상속)인터페이스 타입으로 그 인터페이스를 구현한 클래스 객체를 사용할 수 있음
      • interface Keyboard { } class Logitec_Keyboard implements Keyboard { } class Samsung_Keyboard implements Keyboard { } class Apple_Keyboard implements Keyboard { } public class Main { public static void main(String[] args) { // 인터페이스 타입 배열로 여러가지 클래스들을 한번에 타입 묶음을 할 수 있음 // 인터페이스 타입으로 객체 생성 Keyboard[] k = { new Logitec_Keyboard(), new Samsung_Keyboard(), new Apple_Keyboard(), }; } }
  • 결합도를 낮춰 유지보수성을 늘리는 디자인 패턴으로서의 역할도 수행함
  • Spring Framework에서 인터페이스를 많이 사용함

 

기본

💡 추상 메서드의 집합
필드와 메서드의 제어자가 확정되어 있음
따라서 필드와 메서드 앞 제어자를 생략해도 컴파일러가 자동으로 각각의 제어자를 삽입함

  • 모든 필드: public static final
  • 대부분의 메서드: public abstract
    • 예외: default, static

 

구조

interface 인터페이스명 {
	public static final 자료형 필드명 = 값;
	public abstract 리턴타입 메서드명();
}
ex)
interface A {
	public static final int a = 3;
	public abstract void abc();
}

interface A {
	int a = 3;
	void abc();
}

System.out.println(A.a); // static의 특징: 클래스명이나 인터페이스명으로 접근 가능
A.a = 4; // error! final의 특징: 값 변경 불가능

 

구현

💡 추상 메서드의 집합 → 어떻게 구현해야 할까?
keywrod: implements, 다중 구현

  • 추상 메서드처럼 그 자체로 인스턴스를 생성할 수 없음
    • 인터페이스도 구현부를 만들어주는 클래스에 구현(implements)되어야 함
    • 인터페이스를 상속 받았으면, 자식 클래스에서 인터페이스가 포함하고 있는 추상 메소드를 구체적으로 구현함
  • 특징점
    • 다중 구현
    • implements Animal, Pet
// 인터페이스와 클래스
interface Animal {
	public abstract void cry();
}

interface Pet {
	public abstract void play();
}

class Tail {
	...
}

// 클래스 상속 + 인터페이스 구현 = 동시에 가능, 다중 구현 가능
class Cat extends Tail implements Animal, Pet {
	public void cry(){
		System.out.println("냥");
	}
	public void play(){
		System.out.println("쥐잡기 놀이");
	}
}
  • 자식 클래스에 클래스 상속(extends)와 인터페이스 구현(implements) 동시에 가능
public interface IBehavior {
	public abstract void play();
}

public class Soccer extends Sport implements IBehavior {
	@Override
	public void play() {
		System.out.println("Playing Soccer");
	}
}

 

 

구현

일부만 구현하고 싶다면?

interface Animal {
	void walk();
	void run();
	void breed();
}

// Animal 인터페이스 중 일부만 구현
abstract class Mammal implements Animal {
	public void walk(){...}
	public void run(){...}
}

class Lion extends Mammal {
	@Override
	public void breed(){...}
}

 

인터페이스 - 다중 구현이 가능한 이유

  • 클래스와 다르게 메소드 구현부가 없기 때문에 충돌 가능성이 없음
  • 자손 인터페이스: 조상 인터페이스에 정의된 멤버들을 모두 상속 받음
    • 하지만 필드는 static이기 때문에 구현체를 따라가지 않음
    interface Changeable{
    	void change();
    }
    
    interface Powerable{
    	void power(boolean b);
    }
    
    interface Controlable extends Changeable, Powerable {
    	// 인터페이스끼리 다중 상속 -> 추상 멤버를 그대로 물려받음
    }
    
    // 클래스에 통합된 인터페이스를 구현
    class MyObject implements Controlable{
    	public void change(){
    		System.out.println("채널을 바꾸는 메서드");
    	}
    	public void power(boolean b){
    		System.out.println("전원 온오프를 하는 메서드");
    	}
    }
    
    // 프로그램의 시작점
    public class Main{
    	public static void main(String[] args) {
    	// 인터페이스 다형성 - 업캐스팅
    		Controlable[] o = {new MyObject(), new MyObject()};
    		o[0].change();
    		o[1].power(true);
    		
    		// 각 단일 인터페이스로도 타입으로 사용 가능
    		Changeable inter1 = new MyObject();
    		inter1.change();
    		Powerable inter2 = new MyObject();
    		inter2.power(true);
    	}
    }

 

인터페이스 상수 필드 상속 관계

  • 클래스의 상속일 경우, 클래스 필드 멤버끼리 상속되어 덮어 씌워짐
  • 인터페이스 필드들은 모두 pulbic static final이기 때문에 상속을 해도 독립적으로 운용
// 인터페이스와 클래스
interface Iflower{
	int ex = 10; // public static final
}

interface IPlant extends Iflower{
	int ex = 20; // public static final
}

class Tulip implements IPlant{
	int ex = 30; // 그냥 인스턴스 변수
}

public class Main{
	public static void main(String[] args){
		// 클래스로 접근
		Tulip t = new Tulip();
		System.out.println(t.ex); // 30
		
		// 인터페이스로 접근
		System.out.println(Iflower.ex); // 10
		System.out.println(IPlant.ex); // 20
	}
}

 

default & static 메서드

default 메서드

💡 보통 인터페이스를 구현한 이후, 수정과정에서 인터페이스 모든 구현체에게 수정없이 광역으로 함수를 만들어 주고 싶을 때 사용

  • 앞에 키워드 default를 붙이고, 일반 메서드처럼 구현부{…}가 있어야 함
  • 접근 제어자: public이고 생략 가능
  • 자식 클래스에서 default 메소드를 오버라이딩하여 재정의 가능
interface Calculator {
	int plus(int i, int j);
	int multiple(int i, int j);
	
	default int sub(int i, int j){
		return i-j;
	}
}

class MyCalculator implements Calculator {
	// 추상 메서드만 구현 (default 메서드는 굳이 x)
	@Override
	public int plus(int i, int j) {return i+j;}
	public int multiple(int i, int j) {return i*j;}
}

public class Main{
	public static void main(String[] args) {
		MyCalculator mycal = new MyCalculator();
		Calculator cal = (Calculator) mycal;
		int value = cal.sub(5, 10);
		System.out.println(value); // -5
	}
}

 

  • default 메서드의 다중구현 문제
    • 똑같은 디폴트 메서드를 가진 두 인터페이스 → 하나의 클래스에 구현 ⇒ 아무런 조치를 취하지 않으면 컴파일 에러
    • 인터페이스를 구현한 클래스 → 디폴트 메서드를 오버라이딩하나로 통합
    • 인터페이스의 디폴트 메서드와 부모 클래스 메서드간 충돌이 발생한다면?
      ⇒ 부모 클래스의 메서드가 상속되고 디폴트 메서드는 무시
  • default 메서드의 super
    • 인터페이스명.super.디폴트메서드
interface IPrint {
	default void print(){
		System.out.println("인터페이스의 디폴트 메서드");
	}
}

class MyClass implements IPrint{
	@Override
	public void print(){
		IPrint.super.print(); // 인터페이스 super 메서드 호출
		System.out.println("인터페이스의 디폴트 메서드를 오버라이팅한 메서드");
	}
}

public class Main {
	public static void main(String[] args) {
		MyClass cls = new MyClass();
		cls.print();
		// 인터페이스의 디폴트 메서드
		// 인터페이스의 디폴트 메서드를 오버라이팅한 메서드
	}
}

 

static 메서드

💡 인스턴스 생성과 상관없이 인터페이스 타입으로 접근하여 사용할 수 있는 메서드

interface Calculator {
    public int plus(int i, int j);
    public int multiple(int i, int j);

    // 디폴트 메서드
    default int sub(int i, int j){
        return i - j;
    }

    **// 스태틱 메서드
    public static void explain(){
        System.out.println("interface static 메서드 입니다. 이 인터페이스는 pluc, multipe, sub 기능을 제공하는 메서드를 지니고 있습니다. (설명)");   
    }**
}

class MyCalculator implements Calculator {
    @Override
    public int plus(int i, int j) {  return i + j; }
    @Override
    public int multiple(int i, int j) { return i * j; }
}

public class Main {
    public static void main(String[] args){
        // 클래스 처럼 static 메소드 호출 하면 된다.
        **Calculator.explain(); // "interface static 메서드 입니다. 이 인터페이스는 pluc, multipe, sub 기능을 제공하는 메서드를 지니고 있습니다. (설명)"**
    }
}

 


참고자료

반응형