인터페이스 정의하는 방법
인터페이스란?
극단적으로 동일한 목적 하에 동일한 기능을 수행하게끔 강제하는 것.
즉, 자바의 다형성을 극대화하여 개발 코드 수정을 줄이고 프로그램 유지 보수성을 높이기 위해 사용된다.
인터페이스는 interface 키워드를 통해 선언할 수 있으며, implements 키워드를 통해 일반 클래스에 해당 인터페이스를 적용시킬 수 있다.
JAVA8 이전까지는 상수, 추상 메소드만 선언이 가능하지만, JAVA8부터 디폴트메소드, 정적메소드가 추가되었다.
인터페이스 구현하는 방법
public interface 인터페이스명 {
//상수
타입 상수명 = 값;
// 인터페이스에서 값을 정해주고, 해당 인터페이스를 이용하는 클래스에서는 값을 바꿀 수 없다.
//추상 메소드
타입 메소드명(매개변수, ... );
// 해당 추상 메소드의 가이드에 따라 오버라이딩해 재구현한다.
//디폴트 메소드
default 타입 메소드명(매개변수, ... ){
//구현부
}
// 인터페이스에서 해당 메소드를 기본적으로 제공해주나, 용도에 따라 재구현하여 사용할 수 있다.
//정적 메소드
static 타입 메소드명(매개변수) {
//구현부
}
// 인터페이스에서 구현한 대로 사용된다.
}
디폴트 메소드의 경우 오버라이딩하여 간편하게 사용할 수 있으나, 용도가 달라질 시(또는 구현부의 차이가 생길 시) 오버라이딩하여 재구현할 수 있으므로, 이미 운영되고 있는 시스템에서 추가 요건으로 인해 불가피하게 반영을 해야할 때 사용하면 효과적이다.
// 일반적인 인터페이스 구현
public class studyClass implements study{
@Override
public String interfaceMethod() {
return null;
}
@Override
public String name() {
return null;
}
}
// 일회성 객체를 위한 인터페이스 구현
public class main {
public static void main(String[] args) {
study study = new study() {
@Override
public String interfaceMethod() {
return null;
}
@Override
public String name() {
return null;
}
};
}
}
인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
public class studyClass implements study{
//구현 클래스 메소드
public void say(){
System.out.println("this studyClass");
}
@Override
public String interfaceMethod() {
return null;
}
@Override
public String name() {
return null;
}
}
public static void main(String[] args) {
study study = new studyClass();
studyClass studyClass = new studyClass();
study.say(); // 불가능
studyClass.say();
}
인터페이스 타입으로 객첼르 생성할 수 있으며 해당 객체에 구현 클래스로 인스턴스화할 수 있다.
인터페이스 타입으로 선언한 객체는 구현 클래스 내에서 생성한 메소드, 필드를 사용할 수 없다.
인터페이스 상속
인터페이스의 조상은 인터페이스만이 가능하다(클래스와 달린 Object가 최고 조상이 아니다.)
다중 상속이가능하다.(∵ 추상 메소드는 충돌해도 문제가 생기지 않는다.)
// 구현부가 비었지만 상속 했으므로 멤버가 2개
interface Fightable extends Movable, Attackable { }
interface Movable{ void move(int x, int y); }
interface Attackable { void attack(Unit u); }
인터페이스 vs 상속
extends를 이용하여 다른 객체를 상속받을 경우에는 하나의 클래스만 상속이 가능하다.
implements를 이용하여 인터페이스를 상속받을 경우에는 다수의 인터페이스를 상속이 가능하다.
다형성 비교
상속의 다형성의 기본 형태는 부모클래스 p = new 자식클래스()이다. 이때, 부모클래스 p가 참조할 클래스는 자신의 클래스보다 범위가 넓거나 같아야 한다.
class Car {/*구현*/}
class FireEngine extends Car {/*구현*/}
public static void main(String[] args) {
/*자기 자신을 참조하여 객체 생성*/
Car c = new Car();
/*자기 자신보다 범위가 넓은 자식클래스를 참조하여 객체 생성*/
Car c1 = new FireEngine();
// FireEngine()을 참조하고 있으나 Car의 인스턴스이기에 Car 클래스에 정의된 멤버만 사용 가능
/*형변환 하여 객체 생성*/
FireEngin f = (FireEngine)new Car();
// Car()를 참조하고 있지만 FireEngine()으로 형변호나 하여 FireEngine 클래스의 멤버를 사용할 수 있다.
// 형변환하지 않을 시, 에러가 발생한다.
}
public class InterfaceTestClass implements InterfaceTest1 , InterfaceTest2, InterfaceTest3{/*구현*/}
public static void main(String[] args) {
InterfaceTest1 i1 = new InterfaceTestClass();
InterfaceTest2 i2 = new InterfaceTestClass();
InterfaceTest3 i3 = new InterfaceTestClass();
}
// InterfaceTestClass에는 InterfaceTest1, InterfaceTest2, InterfaceTest3의 멤버가 모두 구현되어 있으나
// 각 i1, i2, i3는 InterfaceTest1, InterfaceTest2, InterfaceTest3의 멤버만을 사용할 수 있다.
인터페이스의 기본 메소드 (Default Method), 자바 8
인터페이스는 기능에 대한 선언만 가능했던 것에 반해, default method를 선언할 경우, 해당 메소드의 로직을 구현할 수 있다.
이는 하위 호환성을 위해 추가되었다.
기존에 존재하던 인터페이스를 이용하여서 구현된 클래스를 만들고 사용하고 있는데 인터페이스를 보완하는 과정에서 추가적으로 구현해야 할 혹은 필수족으로 존재해야 할 메소드가 있다면, 이미 이 인터페이스를 구현한 클래스와의 호환성이 떨어지게 되므로 이를 방지하기 위해 default method로 해당 메소드를 선언한다.
interface MyInterface {
default void printHello() {
System.out.println("Hello World");
}
}
class MyClass implements MyInterface {}
public class DefaultMethod {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.printHello(); //실행결과 Hello World 출력
}
}
인터페이스의 static 메소드, 자바 8
default method와 동일하게 인터페이스 내에서 메소드 로직을 구현할 수 있다.
반드시 클래스 명을 통하여 메소드를 호출해야 하며, default method와 다르게 재정의가 불가능하다.
public interface Calculator {
public int plus(int pre, int post);
public int multi(int pre, int post);
default int execPlus(int pre, int post){
return pre + post;
}
static int execMulti(int pre, int post){ return pre * post; }
}
public class CalculatorImpl implements Calculator {
@Override
public int plus(int pre, int post) {
return pre + post;
}
@Override
public int multi(int pre, int post) {
return pre * post;
}
}
public class InterfaceEx {
public static void main(String[] args) {
Calculator cal = new CalculatorImpl();
int resultPlus = cal.plus(3, 9);
int resultMulti = cal.multi(3, 9);
System.out.println("resultPlus 값은 : " + resultPlus);
System.out.println("resultMulti 값은 : " + resultMulti);
int resultExecPlus = cal.execPlus(3, 9);
System.out.println("dafault method 호출 결과 : " + resultExecPlus);
int resultExecMulti = Calculator.execMulti(3, 9);
System.out.println("static method 호출 결과 : " + resultExecMulti); }
}
※ default, static method를 사용할 때 알아둘 점 3가지
1. 클래스가 항상 이긴다. 클래스나 슈퍼클래스에서 정의한 메소드가 디폴트 메소드 보다 우선권을 가지게 된다.
2. 1번 규칙 이외의 상황에서는 서브인터페이스가 이긴다. 상속 관계를 갖는 인터페이스에서 같은 시그니처를 갖는 메소드를 정의할 때는 서브인터페이스가 이긴다. 즉 B가 A를 상속받는다면, B가 A를 이긴다.
3. 여전히 디폴트 메소드의 우선순위가 결정되지 않았다면 여러 인터페이스를 상속 받는 클래스가 명시적으로 디폴트 메소드를 오버라이드하고 호출하자.
인터페이스의 private 메소드, 자바 9
자바8에서 등장한 default, static 메소드는 분명 편리하지만 아쉬운 부분이 존재한다.
default, static 문제점
1. interface 내부 메소드에서 사용할 부분인데, public method로 선언하는 것이 필수가 된다.
public interface Boo {
/**
* @return "Bye"
*/
default String printBye(){ // interface 내부 메소드 knockDoor 메소드에서 사용
return "Bye";
}
default void knockDoor(){
System.out.println("Ok.. " + printBye());
}
}
/*****************************************************************************/
public static void main(String[] args) {
Boo boo = new Main();
boo.printBye(); // Boo 인터페이스의 내부 메소드 인데, 호출이 가능함.
boo.knockDoor();
}
인터페이스를 구현할 수 있는 인터페이스 또는 클래스가 특정 method에 접근하는 것을 막고 싶을 수도 있다.
public class Main implements Boo{
@Override
public String printBye(){ //상속받은 메서드를 원하지 않게 오버라이딩 한다.
return "Bye";
}
}
이에 대해 private 메소드를 사용해 해결할 수 있다.
public interface Boo {
/**
* @implSpec 이 메소드는 오버라이드 하거나, 외부에서 접근할 수 없습니다.
* @return "Bye"
*/
private String printBye(){
return "Bye";
}
default void knockDoor(){
System.out.println("Ok.. " + printBye());
}
}
public class Main implements Boo{
@Override //Error -> method does not override or implement a method from a supertype
public String printBye(){
return "Bye";
}
}
private String printBye()를 작성함으로 외부에서 접근이 불가능하게 만들어 두는데, 이러한 방법을 통해 메소드의 제한을 둘 수 있다.
ref{
https://limkydev.tistory.com/197
https://velog.io/@oyeon/7-3537-인터페이스의-선언-상속-구현
https://debugdaldal.tistory.com/171
https://siyoon210.tistory.com/95
https://dahyeee.tistory.com/entry/JAVA-interface-default-static메소드
https://catch-me-java.tistory.com/44
}
'Programming > Java' 카테고리의 다른 글
[JAVA]멀티쓰레드 프로그래밍 (0) | 2022.03.14 |
---|---|
[JAVA] 예외 처리 (0) | 2022.03.04 |
[JAVA] 패키지 (0) | 2022.02.21 |
[JAVA]상속 (0) | 2022.02.14 |
[JAVA] 클래스 (0) | 2022.02.06 |
댓글