- 해당 내용은 백기선 님의 자바 온라인 스터디 공부 및 제출 목적
-> https://github.com/whiteship/live-study/issues/8
목표
자바의 인터페이스에 대해 학습하세요.
학습할 것 (필수)
- 인터페이스 정의하는 방법
- 인터페이스 구현하는 방법
- 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
- 인터페이스 상속
- 인터페이스의 기본 메소드 (Default Method), 자바 8
- 인터페이스의 static 메소드, 자바 8
- 인터페이스의 private 메소드, 자바 9
* 인터페이스(Interface)
- Java는 클래스(class)를 단일 상속이 가능한 특성을 가지고 있는데, 이는 객체지향 프로그래밍에서 큰 제약이기 때문에 인터페이스(interface)라는 개념을 도입.
- 인터페이스라는 이름답게 디자인(설계)적인 요소를 위한 유형이므로, 메소드를 정의하지만 기본적으로 구현 코드는 제공하지 않음.
- 인터페이스를 구현받는 클래스에서 메소드의 내용을 구현.
interface와 abstract 클래스를 사용하는 이유
- 설계시 선언해두면, 개발할 때 기능을 구현하는 데에만 집중할 수 있다.
- 공통적인 인터페이스와 abstract 클래스를 선언해 놓으면, 선언과 구현을 구분할 수 있다.
1. 인터페이스 정의하는 방법
- 클래스 작성하는 방법과 동일하다. 다만 키워드로 class 대신 interface를 사용.
- 모든 메소드는 추상 메소드로 간주.
- 인터페이스는 공용 API를 정의하기 때문에, 암시적으로 public 이므로 생략하는 것이 일반적.
- 인터페이스 내에 존재하는 메소드는 무조건 "public abstarct"로 선언되며, 생략 가능
- 인터페이스 내에 존재하는 변수는 무조건 "public static final"로 선언되며, 생략 가능
- 인스턴스 필드를 정의 할 수 없음.
- 인터페이스는 인스턴스화 할 수 없으므로 생성자가 필요 없음.
- 메소드 정의만 있으며, 구현은 없음(구현은 상속받은 클래스에서 구현)
interface Fruit {
int MAX_COUNT = "10";
// public static final MAX_COUNT = "10";
int sale(int count);
// public abstract int sale(int count);
}
2. 인터페이스 구현하는 방법
- implements 라는 키워드를 사용.
- 클래스의 상속과 달리 여러개의 인터페이스를 implements 할 수 있다.
- 인터페이스가 가지고 있는 메소드를 하나라도 구현하지 않으면, 해당 클래스는 추상 클래스가 된다. ( 추상 클래스는 인스턴스를 만들 수 없다. 즉, 메모리에 할당되어 실제 사용될 수 없다.)
- 인터페이스에 정의되어 있는 메소드를 필수적으로 구현해야 한다.
public class Apple implements Fruit {
@Override
public int sale(int count){
System.out.println("사과를 " + count + " 개 팔았습니다.");
return count;
}
public void myName(){
System.out.println("내 이름은 사과");
}
}
public class Banana implements Fruit, Mart, ... {
@Override
public int sale(int count){
System.out.println("바나나를 " + count + " 개 팔았습니다.");
return count;
}
public void myName(){
System.out.println("내 이름은 바나나");
}
// Mart와 기타 인터페이스를 상속받고, 메소드를 선언하지 않으면 해당 파일은 실제 사용되지 않는다.
...
}
3. 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
- 다형성을 통해서 자식 클래스의 인스턴스를 부모타입의 참조변수로 참조하는 것이 가능하다는 것이 확인.
- 객체는 클래스가 아닌 인터페이스로 참조.
public static void main(String[] args){
...
// 추천
List<String> list = new ArrayList<String>();
Map<String, Object> map = new HashMap<String, Object>();
// 비추천(선언이 안되는 것은 아님)
ArrayList<String> list2 = new ArrayList<String>();
HashMap<String, Object> map2 = new HashMap<String, Object>();
...
}
- 인터페이스 타입의 참조변수로 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로 형변환이 가능.
public static void main(String[] args){
Fruit apple = new Apple();
Fruit banana = new Banana();
apple.sale(10); // 사과를 10개 팔았습니다.
banana.sale(5); // 바나나를 5개 팔았습니다.
apple.myName(); // X
banana.myName(); // X
(apple)apple.myName(); // O
(banana)banana.myName(); // O
}
- 위 예제에서 apple과 banana가 바라보는 것은 Fruit 인터페이스이기 때문에 각각의 myName() 메소드는 호출하지 못한다.
- 해당 메소드를 호출하기 위해서는 각 클래스로 캐스팅하여 사용해야 한다.
public static void main(String[] args){
Apple apple = new Apple();
Banana banana = new Banana();
apple.sale(10);
banana.sale(5);
apple.myName(); // O
banana.myName(); // O
}
- 위 예제이서 apple과 banana가 바라보는 것은 자신의 클래스이기 때문에 각각의 myName() 메소드 호출이 가능하다.
4. 인터페이스 상속
- 인터페이스는 인터페이스로부터만 상속을 받을 수 있으며, 다중 상속이 가능.
- 클래스의 상속과 마찬가지로 자식 인터페이스에는 부모 인터페이스에 정의된 멤버를 모두 상속.
- 인터페이스는 클래스와 달리 Object 클래스와 같은 최고 부모 인터페이스가 없다.
// 인터페이스 선언
interface Fruit {
void sale();
}
interface Mart {
void whereSale();
}
--------------------------------------------------------
// 구현 class
public class test implements Fruit, Mart {
@Override
public void sale(){
System.out.println("사과를 팔다");
}
@Override
public void whereSale(){
System.out.println("사과를 마트에서 사다");
}
}
- 위 예제를 살펴보면, test 클래스가 Fruit, Mart 2개의 구현을 받았기 때문에, 각각의 메소드를 구현해주어야만 한다.
// 인터페이스 선언
interface Fruit {
void sale();
}
interface Mart {
void whereSale();
}
interface Apple extends Fruit, Mart {}
--------------------------------------------------------
// 구현 class
public class test implements Apple {
@Override
public void sale(){
System.out.println("사과를 팔다");
}
@Override
public void whereSale(){
System.out.println("사과를 마트에서 사다");
}
}
- 위 예제를 살펴보면, Apple은 Fruit, Mart를 다중 상속을 받았고, test 클래스는 Apple를 implements(구현)을 받았으므로, 상속받은 2개의 메소드를 구현해주어야만 한다.
5. 인터페이스의 기본 메소드 (Default Method), 자바8
- Java 8 에서 부터 도입된 기능으로 구현을 포함하는 메소드를 포함한 인터페이스를 정의 할 수 있다.
- 인터페이스에 메소드를 추가하는 것은, 추상 메소드를 추가한다는 것이고, 이는 해당 인터페이스를 구현한 기존의 모든 클래스에 새로 추가된 메소드를 구현해야 하는 것이다.
- Default Method(디폴트 메소드)는 추상 메소드가 아니기 때문에, 추가되어도 기존 클래스에서 추가된 메소드를 구현하지 않아도 된다.
- 메소드 앞에 default 키워드를 붙이며, 추상 메소드와 달리 { }가 있어야 한다.
- 상속받은 인터페이스의 메소드도 Default Method 로 정의 할 수 있다.
interface Fruit {
void whereSale();
default void sale(){
System.out.println("과일을 팔다")
}
}
--------------------------------------------
// 구현 class
public class Apple implements Fruit {
@Override
public whereSale(){
System.out.println("시장에서 사과를 팔다");
}
// Default Method 는 생략 가능
}
--------------------------------------------
public static void main(String[] args){
Fruit Apple = new Apple();
apple.sale(); // "과일을 팔다"
}
interface Fruit {
void whereSale();
default void sale(){
System.out.println("과일을 팔다")
}
}
--------------------------------------------
// 구현 class
public class Apple implements Fruit {
@Override
public whereSale(){
System.out.println("시장에서 사과를 팔다");
}
// Default Method 는 재선언 가능
@Override
public sale(){
System.out.println("사과를 팔다")
}
}
--------------------------------------------
public static void main(String[] args){
Fruit Apple = new Apple();
apple.sale(); // "사과를 팔다"
}
6. 인터페이스의 static 메소드, 자바8
- 인스턴스 생성과 상관없이 인터페이스 타입으로 호출하는 메소드.
- static 키워드를 사용하며, 접근제어자는 항상 public 이지만 생략 가능.
- { } 가 있어야 한다.
- 재선언(Override)이 불가능하다.
interface Fruit {
void whereSale();
static void sale(){
System.out.println("과일을 팔다")
}
}
--------------------------------------------
// 구현 class
public class Apple implements Fruit {
@Override
public whereSale(){
System.out.println("시장에서 사과를 팔다");
}
// Default Method 는 재선언 불가능
/*
@Override
public sale(){
System.out.println("사과를 팔다")
}
*/
}
--------------------------------------------
public static void main(String[] args){
Fruit Apple = new Apple();
//apple.sale(); // Error
Fruit.sale(); // "과일을 팔다"
}
- static 메소드를 사용할 경우, 기존 클래스의 static 메소드처럼 class명.메소드 "apple.sale()"로 호출하는게 아닌, interface명.메소드"Fruit.sale()"로 호출해야 한다.
7. 인터페이스의 private 메소드, 자바9
- Java 9 에서부터 인터페이스에서 private methods 사용이 가능.
- Default Methods 가 동일한 구현을 가지고 있다면 이 부분을 private method로 따로 분리하여 처리가 가능.
- { } 가 있어야 하고, abstract(추상적)이 아니다.
- 구현체에서 구현할 수 없고 자식 인터페이스에서 상속이 불가능.
- static 메소드도 private이 가능.
interface Fruit {
void whereSale();
default void fruitMethod(){
sale();
}
private void sale(){
System.out.println("과일을 팔다")
}
}
--------------------------------------------
// 구현 class
public class Apple implements Fruit {
@Override
public whereSale(){
System.out.println("시장에서 사과를 팔다");
}
}
--------------------------------------------
public static void main(String[] args){
Fruit Apple = new Apple();
apple.fruitMethod(); // "과일을 팔다"
}
참고
- https://blog.baesangwoo.dev/posts/java-livestudy-8week/
- https://dev-coco.tistory.com/13?category=962739
- https://yadon079.github.io/2021/java%20study%20halle/week-08
'Study > Java(Online-Study)' 카테고리의 다른 글
9주차 과제: 예외 처리 (0) | 2021.08.07 |
---|---|
7주차 과제 : 패키지 (0) | 2021.07.19 |
6주차 과제: 상속 (0) | 2021.07.17 |
5주차 과제: 클래스 - 이론(1) (0) | 2021.06.27 |
4주차 과제: 제어문 - 이론(1) (0) | 2021.06.20 |