https://github.com/whiteship/live-study/issues/8
목차
1. 인터페이스 정의하는 방법
2. 인터페이스 구현하는 방법
3. 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
4. 인터페이스 상속
5. 인터페이스의 기본 메소드 ( Default Method ), 자바8
6. 인터페이스의 static 메소드, 자바8
7. 인터페이스의 private 메소드, 자바9
1. 인터페이스 정의하는 방법
인터페이스는 키워드 interface를 이용해서 정의한다.
public interface GroupedInterface extends Interface1, Interface2, Interface3 {
void doSomething(int i, double x);
int doSomethingElse(String s);
}
인터페이스는 추상메서드의 집합으로 추상클래스보다 추상화 정도가 높다. 추상클래스와 다르게 instance variable과 생성자를 갖지 않는다. 인터페이스는 그 자체로는 사용할 수 없고 다른 클래스를 만드는 기본 설계도로 사용된다.
abstract methods는 선언부만 갖고 있기 때문에 {}를 사용하지 않고 대신 세미콜론을 붙인다. 또한 default methods와 static methods를 가질 수 있다. 예제에서는 doSomething과 dosomethingElse에 접근제어자가 없는데, 모든 메서드는 public abstract이어야 한다. 하지만 생략해도 컴파일러가 자동적으로 추가해준다. 또한 모든 멤버변수는 public static final이어야 하고 마찬가지로 생략할 수 있다.
2 & 3 인터페이스 구현하는 방법, 레퍼런스를 통해 구현체를 사용하는 방법
인터페이스를 사용하려면 인터페이스를 구현한 클래스를 작성해야한다. 이 때 extends가 아닌 implements를 사용한다.
public interface Relatable {
public int isLargerThan(Relatable other);
}
public class RectanglePlus implements Relatable {
/* 내용 생략 */
public int isLargerThan(Relatable other) {
RectaglePlus otherRect = (RectanglePlus)other;
/* 내용 생략 */
}
}
크기를 비교하고 싶은 메서드를 작성한다고 하자. 위 예제에서 isLargerThan의 parameter로 Relatable interface를 사용하고 있는데, 이는 인자로 Relatable interface를 구현한 클래스를 받겠다는 뜻이다. isLargerThan으로 위의 예제에서는 직사각형을 받았고 내용은 생략됐지만 넓이를 비교했다. 그런데 직사각형뿐만 아니라 book 클래스의 page를 비교할 수도 있고 students 클래스의 학생 수를 비교할 수도 있다. 또한 String의 character 수를 비교할 수도 있다.
만약에 인터페이스를 사용하지 않는다면 각기 다른 parameter를 갖는 여러 메서드를 오버로딩해서 만들어야한다. 왜냐하면 book, students, String이 서로 상속관계에 있는 클래스면 좋겠지만, 상속관계에 있지 않은 클래스에서 공통적으로 사용하는 메서드가 있을 수 있기 때문이다. 이 때 인터페이스의 장점이 나오는데, 인터페이스는 서로 상속관계에 있지 않은 서로 다른 클래스들을 연관지을 수 있다.
그리고 어떤 인터페이스를 구현했는지를 보면 그 클래스가 어떤 역할을 하는지 유추할 수 있다는 장점도 있다.
public interface doIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
}
public abstract doItClass implements doIt {
void doSomething(int i, double x) {
/* 내용생략 */
}
}
만약 인터페이스의 일부만을 구현하면 추상클래스로 선언해야한다. 이와 관련된 문제는 default 메소드에서 다룬다.
4. 인터페이스의 상속
자바는 단일상속만 허용하지만 인터페이스는 클래스와는 다르게 다중상속을 허용한다. 인터페이스는 구현부는 없고 선언부만 있기 때문에 클래스에 비해 충돌문제가 없다.
public interface Movable {
void move(int x, int y);
}
public interface Attackable {
void attack(Unit u);
}
public interface Fightable extends Movable, Attackable {}
상속을 받을 때는 클래스와 마찬가지로 extends를 사용하는데, Fightable에는 구현부에 정의된 메서드가 없지만 Movable과 Attackable에 정의된 메서드를 포함하고 있다.
5. 인터페이스의 기본 메소드
만약 아래 코드처럼 doItclass가 doIt을 구현했는데 인터페이스를 변경해서 doSomethingElse라는 메서드를 작성했다고 하자. 그러면 아래의 doItClass 또한 doSomethingElse를 새롭게 구현해야한다.
public interface doIt {
void doSomething(int i, double x);
int doSomethingElse(String s);
}
public doItClass implements doIt {
void doSomething(int i, double x) {
/* 내용생략 */
}
}
Default method를 사용하면 인터페이스를 변경해도 구현한 클래스를 변경할 필요가 없다. 디폴트메서드는 키워드로 default를 사용한다. 접근제어자는 마찬가지로 public을 사용해야한다. Default method는 충돌문제가 있을 수 있는데 다음과 같은 규칙으로 해결한다.
1. 여러 인터페이스의 디폴트 메서드 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
- 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
Reference
1. 자바의 정석 3판
2. 오라클 튜토리얼
피드백
1. 라이브러리에서 어떤 클래스가 상속을 받으면, 그 클래스의 사용자가 상속을 이용할 수 없다.
2. default method 오버라이딩 예시
public class HelloJoinMember implements JoinMember, JoinGroup {
@Override
public void preJoin() {
System.out.println("Pre join Hello member");
}
@Override
public void afterJoin() {
JoinMember.super.afterJoin();
JoinGroup.super.afterJoin();
}
}
default method가 충돌이 날 때, 겹치는 method를 재정의해야된다. 위의 preJoin처럼 새롭게 재정의해도 되고, 밑의 afterJoin처럼 JoinMember와 JoinGroup의 method를 사용해도 된다.
그리고 위의 super는 기존의 알고 있던 super와는 다르다. 왜냐하면 afterJoin이 static method가 아니기 때문이다.
3. static method는 오버라이딩이 안된다. 인스턴스 메서드와 static 메서드는 다른 영역으로 접근해야하기 때문이다. 그리고 default method와 마찬가지로 인터페이스에 static method를 추가해도 인터페이스를 구현한 클래스를 수정할 필요가 없음 예를 들면 자바9에 추가된 List의 of method
4. 강한 결합, 느슨한 결합
인터페이스를 사용하는 중요한 이유중 하나다. Client에서 사용하는 클래스를 얼마나 수정해애하는가에 대한 이야기다.
class A {
public void methodA(B b) {
b.methodB();
}
}
class B {
public void methodB() {
System.out.println("methodB()");
}
}
만약 위의 코드에서 methodA가 B클래스가 아닌 C클래스의 methodB를 사용하고 싶다면 A클래스의 선언부를 C를 사용하게끔 수정해야 한다. 위와 같은 경우를 강한결합이라고 하는데 좀 더 유연하고 A를 수정하지 않고도 C를 사용하려면 느슨한 결합을 사용하면 된다.
class A {
public void methodA(I i) {
i.play();
}
}
class B implements I {
public void play() {
System.out.println("play in B method");
}
}
인터페이스 I와 다른클래스 C를 생략했다. methodA의 파라미터 I i의 의미는 인터페이스를 구현한 클래스를 인자로 받겠다는 의미이다. 그래서 강한 결합과 다르게 B가 아닌 클래스 C를 사용하고 싶으면 클래스 A를 수정하지 않고도 인터페이스 I를 구현한 C를 새롭게 추가해서 사용할 수 있다.
5. 그래서 인터페이스를 사용하면 되는데, 추상클래스는 왜 사용하는가?
인터페이스는 상수만 가질 수 있고 private 변수를 가질수 없다. 인터페이스의 default method로 구현할 수 없는 코드가 존재한다. 그리고 자바 버전8 이전에는 private method가 안되니까 인터페이스의 중복코드를 추상클래스로 옮기고 중복코드를 제거할 수도 있다.
'자바 > 백기선 자바스터디' 카테고리의 다른 글
[백기선 자바스터디] enum (0) | 2022.10.10 |
---|---|
[백기선 자바스터디] 예외 처리 (0) | 2022.09.21 |
[백기선 자바스터디] 패키지 (0) | 2022.07.25 |
[백기선 자바스터디] 상속 (0) | 2022.07.14 |
[백기선 자바스터디] 클래스 (0) | 2022.07.06 |