8주차 과제: 자바 인터페이스
목표: 자바의 인터페이스에 대해 학습하기
- 인터페이스 정의하는 방법
- 인터페이스 구현하는 방법
- 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
- 인터페이스 상속
- 인터페이스의 기본 메소드 (Default Method), 자바 8
- 인터페이스의 static 메소드, 자바 8
- 인터페이스의 private 메소드, 자바 9
인터페이스란, 자바가 가지고 있는 제약 중 하나인 다중 상속이 불가능 한 제약조건을 해결하기 위해 도입된 개념으로, 각 클래스 사이에서 클래스를 엮어주는 역할을 하며 추상 메소드를 선언함으로써 클래스 간의 통일성을 부여한다.
인터페이스를 사용하는 이유는 무엇일까?
인터페이스를 정의할 때에는, 2가지 특성을 고려하여 코드를 작성해야 한다.
body
부분이 존재하지 않는 abstract method
만 선언할 것다음은 인터페이스의 예다.
public interface Pencil {
public int PENCIL_LENGTH = 15;
public String PENCIL_BRAND = "JEDO";
public void writeEnglish(String englishSentence);
public void writeKorean(String hangulSentence);
public void broke();
}
public class Student implements Pencil{
public static void main(String[] args) {
System.out.println("this is student class");
}
@Override
public void writeEnglish(String englishSentence) {
System.out.println("this is English sentence");
}
@Override
public void writeKorean(String hangulSentence) {
System.out.println("this is Korean sentence");
}
@Override
public void broke() {
System.out.println("Ouch! it's broken");
}
}
Pencil
이라는 인터페이스를 선언하고, Student
클래스에서 이를 implements
하여 인터페이스에서 선언한 추상 메소드들을 사용할 수 있도록 했다.
인터페이스를 구현하기 위해서, 먼저 클래스에서 implements
예약어를 사용해야 한다. 앞서 1장에서 예시로 보여준 Student
클래스에서도 사용했다.
implements
할 수 있다)인터페이스에 선언된 메소드들을 override
하지 않으면, 해당 클래스는 추상 클래스가 된다.
public interface Pencil {
public int PENCIL_LENGTH = 15;
public String PENCIL_BRAND = "JEDO";
public void writeEnglish(String englishSentence);
public void writeKorean(String hangulSentence);
public void broke();
}
public class Student implements Pencil{
public static void main(String[] args) {
System.out.println("this is student class");
}
@Override
public void writeEnglish(String englishSentence) {
System.out.println("this is English sentence");
}
@Override
public void writeKorean(String hangulSentence) {
System.out.println("this is Korean sentence");
}
@Override
public void broke() {
System.out.println("Ouch! it's broken");
}
}
예제에서도 Student
클래스는 Pencil
인터페이스에서 선언한 모든 메소드들을 override
하여 사용하는 것을 확인할 수 있다. 만약, 1개 라도 override
하지 않는다면 에러가 발생한다.
인터페이스 레퍼런스를 통해 구현체를 사용하는 방법은 실 예제를 보며 익혀보자.
다음과 같이 Car
인터페이스가 존재하고, Morning
Sonata
클래스가 이를 implements
한다.
public interface Car {
public void speakCarName();
public void speakCarBrand();
}
public class Morning implements Car{
@Override
public void speakCarName() {
System.out.println("I am Morning");
}
@Override
public void speakCarBrand() {
System.out.println("I'm in Kia!");
}
}
public class Sonata implements Car{
@Override
public void speakCarName() {
System.out.println("I am Sonata");
}
@Override
public void speakCarBrand() {
System.out.println("I'm in Hyundai!");
}
}
public class CarExam {
public static void main(String[] args) {
Morning morning = new Morning();
Sonata sonata = new Sonata();
sonata.speakCarName();
sonata.speakCarBrand();
morning.speakCarName();
morning.speakCarBrand();
}
}
// 결과 값:
I am Sonata
I'm in Hyundai!
I am Morning
I'm in Kia!
Car
인터페이스에서 선언한 speakCarName()
speakCarBrand()
메소드를 override
하여 정상적으로 사용함을 확인할 수 있다.
다음과 같이 CarExam
클래스의 내용을 조금 변경했을 때
인터페이스 레퍼런스를 통해 구현체를 사용하는 것을 확인할 수 있다. (Car
)
public class CarExam {
public static void main(String[] args) {
Car morning = new Morning();
Car sonata = new Sonata();
showBrand(morning);
showBrand(sonata);
}
public static void showBrand(Car car) {
car.speakCarBrand();
}
}
// 결과 값:
I'm in Kia
I'm in Hyundai!
인터페이스 상속이란, 자식 클래스가 부모 클래스를 상속받는 것과 비슷하게 작동한다. (클래스와 마찬가지로 extends
예약어를 사용한다)
부모 인터페이스에 선언된 추상 메소드를 자식 인터페이스에서 상속받은 뒤, 새로운 추상 메소드나 상수를 추가한다.
예제는 다음과 같다.
public interface A {
int LENGTH = 10;
public void speak();
}
public interface B extends A{
@Override
void speak();
void run();
int LENGTH = 20;
}
public class InheritanceExam implements B{
@Override
public void speak() {
System.out.println("Hello!!");
}
@Override
public void run() {
System.out.println("running!!!!");
}
public static void main(String[] args) {
A a = new InheritanceExam();
B b = new InheritanceExam();
System.out.println(a.LENGTH);
System.out.println(b.LENGTH);
}
// 결과 값:
10
20
예제에서 보는 것 처럼 InheritanceExam
클래스는 A
인터페이스를 implements
하지 않았지만, B
인터페이스가 A
인터페이스를 상속받았기 때문에 A
인터페이스에 존재하는 speak()
까지 override
해야 한다.
인터페이스의 기본 메소드란, 자바 8
이 등장하며 새롭게 정의된 인터페이스로 선언과 동시에 구현할 수 있는 메소드를 말한다.
(다이아몬드 문제
가 발생할 수 있으니 명확히 작성해야 한다)
public interface Animal {
public void speak();
default void findMyFriend(String friendName) {
System.out.println("Where is " + friendName+ "?") ;
}
}
public class Cat implements Animal{
@Override
public void speak() {
System.out.println("Meow!");
}
public static void main(String[] args) {
Animal cat = new Cat();
cat.findMyFriend("Dog");
}
}
// 결과 값:
Where is Dog?
위 예제처럼 인터페이스에서 구현부까지 작성할 수 있도록 해주는 개념이다.
Object
클래스가 제공하는 메소드들은 기본 메소드로써 사용이 불가하다.인터페이스의 static 메소드란, 앞서 5장에서 설명한 default
메소드처럼 자바 8에서 새롭게 등장한 개념으로, override
가 불가능한 메소드를 뜻한다.
public interface A {
public void speak();
public static void run() {
System.out.println("run!!!!!");
}
}
public class Exam implements A{
@Override
public void speak() {
System.out.println("Hello");
}
public void run() {
System.out.println("what is this?");
}
public static void main(String[] args) {
A a = new Exam();
// a.run(); <-- 에러 발생
A.run();
}
}
// 결과 값:
run!!!!!
interface 이름.static 메소드 이름
의 형식으로 접근해야 사용 가능하다.인터페이스의 private 메소드란, 자바 9에서 새롭게 등장한 개념으로, 자바 8에서 등장한 default
, static
메소드가 등장한 후의 메소드들의 관리에 사용한다.
public interface I {
private static String speak() {
return "Hello!";
}
private static void run() {
System.out.println("Run!!!!");
}
public static void speakMyName(String name) {
System.out.println(speak() + " My name is "+ name + "!");
}
}
public class Exam implements I {
public static void main(String[] args) {
I i = new Exam();
// i.run();
// i.speak(); <-- 둘 다 에러 발생
I.speakMyName("GGOB2");
}
}
// 결과 값:
Hello! My name is GGOB2!
다른 default
, static
메소드처럼 구현부를 작성할 수 있으며 static
키워드를 붙힐 수 있다.