[Java] 객체 지향 프로그래밍

Kim Hyen Su·2024년 3월 15일
0

📐Java

목록 보기
8/18

🧊 클래스

1. 객체지향 프로그래밍 이해하기

객체지향 프로그래밍을 이해할 때, 코드의 재사용성, 유지보수성 그리고 중복된 코드의 제거의 세가지 관점에서 이해해야 합니다.

객체지향 프로그래밍의 특징

객체지향 프로그래밍의 대표적인 특징 4가지가 있습니다.

  1. 캡슐화
  2. 상속
  3. 다형성
  4. 추상화

캡슐화

필드와 메서드를 하나로 묶어 객체로 만든 후에 내부 구현 내용을 외부에서 알 수 없게 캡슐화 해주는 것을 의미합니다.

이를 위해서는 접근제어자를 통해 외부로 부터의 접근을 제한합니다.

장점 : 코드가 외부에 노출되지 않아 보안적으로 안전하며, 외부에서 임의로 필드값을 변경하는 것을 막아준다는 장점이 있습니다.

상속

상속이란 상위 클래스의 필드와 메서드를 하위 클래스에게 '물려줌(상속)' 이라고 합니다.

이를 통해서 얻는 이점은 다음과 같습니다.

각각의 상속관계로 묶음으로써 객체 간의 구조를 파악하기 쉽습니다.

상위 클래스로 부터 물려 받은 필드 또는 메서드는 상위에서 한번만 수정하면 하위 클래스에서도 수정된 로직을 사용할 수 있기 때문에 코드의 일관성 유지가 쉽습니다.(유지보수성)

상위 클래스에서 가진 필드와 메서드를 가져다 쓰기 떄문에 하위 클래스에서 중복된 코드를 줄일 수 있고, 코드의 재사용성이 높아집니다.

다형성

다형성은 매개변수 또는 반환값으로 다양한 형태의 객체를 사용할 수 있도록 하여 코드의 재사용성과 유연성을 증가시키는 것입니다.

참조형 변수의 type casting이 핵심입니다. 즉, 클래스 상속관계 또는 인터페이스와 구현체 관계에서 상위클래스 또는 인터페이스를 구현한 하위 클래스들은

상위 클래스 또는 인터페이스 타입으로 자동 형변환이 가능하여 다양한 하위 클래스로 변형이 가능함을 의미합니다.

추상화

객체에서 공통된 필드 또는 메서드를 상위 개념으로 선언하는 것을 추상화(공통화) 라고 합니다.

객체와 클래스 관계

클래스는 객체를 생성하기 위해 코드로 정의해놓은 것으로, 일반적으로 설계도라고 말합니다. 클래스를 통해서 생성된 객체를 '인스턴스'라고 하며, 인스턴스의 생성 과정을 '인스턴스화' 라고 합니다.

클래스의 또다른 정의

객체를 생성하기 위한 설계도는 객체지향 프로그래밍 관점에서의 의미이고, 데이터 저장 관점에서 보면 의미가 다릅니다.

클래스는 데이터들과 함수의 결합을 말합니다.

데이터 저장 개념의 발전 과정은 다음과 같습니다.

  1. 변수 - 하나의 데이터를 저장하는 공간.
  2. 배열 - 같은 타입의 데이터들을 저장하는 연속된 공간.
  3. 구조체 - 서로 관련된 여러 데이터들을 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간.
  4. 클래스 - 데이터와 함수의 결합 (구조체 + 함수)

즉, 데이터 저장 관점에서 클래스는 데이터들과 함수를 함께 저장할 수 있는 결합체라고 볼 수 있습니다.

2. 클래스 설계

클래스 구조

  • 클래스 선언부
  • 필드
  • 생성자
  • 메서드
public class 클래스명 {
	//필드
	//생성자
	//메서드
	//이너 클래스
}

인스턴스 생성

생성식

타입 변수명 = new 클래스명();

new 연산자와 생성자 메서드를 함께 호출하면, 객체를 생성할 수 있습니다.

생성식으로 생성된 객체는 주소값을 반환하기 때문에, 주소를 담을 참조 변수가 필요합니다.

이때, 참조 변수의 타입과 객체 생성식의 클래스 타입은 동일해야 합니다.

인스턴스 사용

인스턴스명.메서드() 또는 인스턴스명.필드명

3. 객체의 속성 : 필드

필드는 객체의 상태 데이터를 저장하는 공간입니다.

필드는 초기화하지 않을 경우, 객체가 생성될 때 해당 타입의 초기값으로 자동 초기화 됩니다.

필드 타입별 기본값

  • byte - 0
  • char - \u0000
  • short - 0
  • int - 0
  • long - 0L
  • float - 0.0f
  • double - 0.0
  • boolean - false
  • 참조형 - null

클래스에 선언된 필드들이 객체가 생성될 때, 객체에 할당되며 일반적으로 우리가 말하는 필드는 객체에 할당된 필드를 말합니다.

필드에 외부에서 접근하기 위해서는 객체를 생성한 뒤 접근이 가능합니다.(일반적으로 캡슐화를 위해서 필드를 private 접근 제어자로 선언하기 때문에 직접 접근은 어렵습니다.)

final 필드와 상수

final 필드는 초기화 시에 저장된 값을 수정할 수 없는 절대값을 의미합니다.

이러한 성질 때문에 선언과 동시에 반드시 초기화 해줘야 하는 변수입니다.

상수 : static final SANGSU;

상수의 특징 : 값이 반드시 한개이며 불변의 값을 가져야 합니다.

4. 객체의 행위 : 메서드

메서드 구조

(접근 제어자) (static) 리턴타입 메서드명((매개변수),...){ 실행 로직 }

메서드 사용

내부에서 접근시에는 객체를 생성할 필요가 없지만, 외부에서 호출 시에는 객체를 생성한 뒤 객체의 참조변수에서 호출해야 합니다.

클래스명 변수명 = new 클래스명();
변수명.메서드명(매개변수,...); // 메서드 호출(사용)

일반적으로 메서드 호출은 외부 메서드 안에서 호출하는 경우가 많습니다. 메서드가 호출되면, 지금까지 실행중이던 메서드는 실행을 잠시 멈추고 호출된 메서드 내부 로직부터 실행하게 됩니다.

지역변수

메서드 내부에 선언한 변수를 의미합니다.

메서드가 실행될 때마다 독립적인 공간을 할당받습니다.

지역변수의 생명주기는 메서드 호출 시, 할당된 뒤 메서드 종료(닫힘 중괄호) 시에 소멸됩니다.

메서드 오버로딩

오버로딩은 함수가 하나의 기능만을 구현하는 것이 아니라 하나의 메서드 이름으로 여러 기능을 구현하도록 하는 Java의 기능입니다.

즉, 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도, 매개 변수의 개수 또는 타입, 순서가 다르면 동일한 이름을 사용해서 메서드를 정의할 수 있습니다.

오버로딩의 가장 큰 예시로 ''println() '' 이 있습니다. 이는 매개변수로 어떠한 값을 받더라도 콘솔에 출력이 가능합니다.(배열과 같은 일부 예외 제외)

이는 실제 내부 코드로 들어가보면 println 이라는 메서드가 오버로딩되어 byte ~ String까지의 매개변수를 받도록 정의되어 있기 때문에 사용이 가능했던 겁니다.

이처럼 오버로딩을 사용하면 우선적으로 메서드명을 하나로 여러 기능을 할 수 있다는 장점이 있습니다.

Call By Value & Call By Reference

메서드 호출 시, 매개변수로 기본형 타입인지 참조형 타입인지에 따라 그 결과가 다르게 나타납니다.

Call By Value

이는 메서드 호출 시, 매개변수가 기본형인 경우에 해당 값을 메모리에 복사하여 넘겨주게 됩니다. 이는 원래 메서드를 호출한 지역에서의 변수의값에는 영향을 미치지 않지만, 메모리 공간을 할당해야 하기 때문에 메모리를 더 사용하게 됩니다.

하지만 다음과 같은 상황도 있습니다.

Call By Reference

이는 메서드 호출 시 매개변수에 참조형 타입인 경우에 해당 객체의 주소값을 담기 때문에, 실제 원본 데이터에 영향을 미치게 됩니다.

5. 인스턴스 멤버와 클래스 멤버

인스턴스 멤버와 클래스 멤버의 가장 큰 차이점은 인스턴스 멤버는 객체를 생성한 후에 접근이 가능하지만, 클래스 멤버는 객체 생성없이 바로 접근이 가능하다는 점입니다.

인스턴스 멤버

객체를 생성해야 접근이 가능한 인스턴스 멤버에는 인스턴스의 필드와 메서드가 있습니다.

하지만, 여기서 주의할 점은 인스턴스 멤버는 객체 생성 시마다 메모리를 할당 하지만, 메서드는 로직이 동일하기 때문에 객체 생성 시마다 메모리를 할당한다면 중복이 발생하여 메모리 효율이 많이 떨어지게 됩니다.

이를 방지하기 위해서 실제 메서드의 경우, 메서드 영역 이라는 곳에 저장하여 모든 인스턴스가 해당 메서드를 공유하여 사용합니다.

클래스 멤버

클래스 멤버는 객체 생성과는 무관하다고 위에 언급하였습니다. 이는 클래스 멤버는 클래스 로더에 의해서 클래스가 메모리에 로드될 때, 함께 초기화 되기 때문입니다.

즉, 이러한 클래스 멤버는 클래스 로드와 함께 고정된 위치에 있게 됩니다.

이로 인해서, 객체 생성과는 상관없이 접근이 가능합니다.

클래스 멤버를 사용하기 위해서는 클래스를 정의할 때, 해당 필드나 메서드에 static 예약어를 붙여주면 됩니다.

이는 정적요소라고도 부르는데 뒤에서 추가로 설명하도록 하겠습니다.

여기서 클래스 멤버 사용 시 주의 사항이 있습니다.

주의사항

클래스 멤버 안에서는 인스턴스 멤버를 사용할 수 없습니다.

이는 멤버가 초기화 되는 시점을 알면 이해하기 쉽습니다. 클래스 멤버는 클래스 로딩되는 시점에 초기화 되기 떄문에, 동적으로 생성되는 인스턴스 멤버가 초기화 되는 시점보다 빠릅니다.

그렇다면, 그 반대로 인스턴스 멤버에서는 클래스 멤버를 사용할 수 있을까요? 넵 가능합니다. 인스턴스 멤버가 초기화 되는 시점에 클래스 멤버는 이미 초기화된 상태이기 때문입니다.

클래스 멤버 사용

클래스명.메서드();

6. 생성자

생성자는 객체 생성 시 호출되며 생성된 객체의 주소를 반환하는 메서드입니다.

일반적으로 생성자를 호출 시에 객체의 필드 값을 초기화 해줍니다.

기본 생성자

매개변수와 중괄호가 비어있는 생성자.

생성자는 객체를 생성할 때 필수적으로 필요한 메서드이기 때문에 클래스 정의 시 생성자를 별도로 정의하지 않아도 컴파일 시점에 컴파일러가 자동으로 기본 생성자를 추가해줍니다.

만약, 생성자가 하나 이상 정의되어 있는 경우, 컴파일러는 기본 생성자를 추가하지 않습니다.

생성자 오버로딩

생성자를 통해 필드를 초기화할 때, 생성자 오버로딩을 많이 사용합니다.

이 때, 주의할 점은 오버로딩이 잘못 적용된 경우에 메서드 중복 오류가 나타날 수 있으니 오버로딩 규칙(매개변수를 다르게 하자!)을 잘 지켜서 정의하는 것이 중요합니다.

7. this 와 this()

this : 인스턴스 자신을 참조하는 참조값을 가진 예약어 입니다.

객체 생성자 및 메서드에서 인스턴스 자신에게 접근 시 사용됩니다.

다음과 같은 상황에서 this 를 많이 사용합니다.

// 생성자 메서드
public A(int a1, int a2){
	this.a1 = a1;
	this.a2 = a2;
}

위의 경우, 매개변수의 이름과 인스턴스 필드명이 같은 경우, 매개변수의 값으로 필드를 초기화해주기 위해 이를 this로 구분해줍니다.

this를 표기하지 않으면, 컴파일러는 이름만 가지고 판단을 할 수가 없게됩니다.

또한, 인스턴스 자신의 참조값을 반환하는 경우에도 사용됩니다.

public A returnInstance(){
	return this;
}

this(...) : 인스턴스 자신의 생성자를 호출하는 예약어 입니다.

객체 내부 메서드에서 자신의 생성자를 호출할 때 사용합니다.

일반적으로 다음과 같은 상황에서 많이 사용됩니다.

public Car(String model) {
    this(model, "Blue", 50000000);
}

public Car(String model, String color) {
    this(model, color, 100000000);
}

public Car(String model, String color, double price) {
    this.model = model;
    this.color = color;
    this.price = price;
}

주의할점

this() 예약어를 호출할 때에는 반드시 호출하는 메서드의 첫 줄에 작성되어야 합니다. 안그러면 오류가 발생해요~

8. 접근 제어자

제어자는 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여해줍니다.

  • 접근 제어자 : public, private, ...
  • 그 외 제어자 : static, abstract, final

접근 제어자란 외부에서 해당 선언된 클래스, 변수, 메서드에 접근을 제어하는 역할을 합니다.

별도의 접근제어자를 지정하지 않은 상태는 'default' 상태 입니다.

  • public : 외부 접근 가능, 내부 접근 가능
  • protected : 동일 패키지 내에서 접근 가능, 다른 패키지에서 상속관계의 경우 접근 가능, 내부 접근 가능
  • default : 같은 패키지 내에서만 접근 가능, 내부 접근 가능
  • private : 내부 접근 가능

요소마다 사용 가능한 접근 제어자

  • 클래스 : public, default
  • 필드 & 메서드(생성자) : public, default, protected, private
  • 지역 변수 : 없음

접근제어자를 이용한 캡슐화(은닉화)

접근제어자를 통해 외부에서 값 변경이 불가능하도록 막기 위해 클래스 내부 멤버들을 캡슐화 합니다.

private 접근 제어자를 통해 제한합니다.

생성자의 접근제어자를 private으로 설정하게 되면, 해당 클래스의 객체 생성할 수 없게 됩니다.

Getter & Setter

클래스 내부 private 으로 정보은닉된 멤버 변수를 외부 코드에서 사용하기 위해서 정의하는 public의 메서드의 통칭입니다.

getXX() = getter = 캡슐화된 필드 조회.

setXX() = setter = 캡슐화된 필드 변경.

9. 패키지와 Import

패키지 : 클래스를 식별하기 위한 용도로 사용됩니다.

일반적으로 상위 패키지와 하위 패키지, 그리고 클래스는 도트로 구분합니다.

클래스의 full name은 패키지를 포함한 클래스명입니다.

예를 들어, pack 이라는 패키지의 클래스(Class1)의 fullname은 pack.Class1 입니다.

import : 다른 패키지에 있는 클래스를 사용하기 위해 명시하는 예약어입니다.

일반적으로 해당 패키지 하위에 정의된 다수의 클래스를 사용 시, ' * '로 표기하여 임포트 해주면 전부 사용이 가능합니다.

<br>

🧊 상속

객체지향 프로그래밍에서 상속은 핵심 개념 중 하나로, 상위 클래스의 필드와 메서드를 하위 클래스에게 물려주는 것을 말합니다.

상속을 사용하게 되면, 유지보수성이 높아지고, 코드 중복이 줄어들어 재사용성이 증가하게 됩니다.

1. 클래스간의 관계와 상속

클래스 간의 상속에서는 extends 라는 예약어를 통해 수행됩니다.

public class 하위클래스 extends 상위클래스 { ... }

상위 클래스와 하위 클래스 간의 관계

상위 클래스와 하위 클래스라고 해서, 상위 클래스가 더 큰 범주에 있는 클래스 같지만, 사실 반대입니다.

하위 클래스 내부에 상위 클래스의 필드와 메서드를 가지며, 이를 확장해 나가는 의미의 하위 클래스가 정의된 것이기 떄문입니다.

쉽게 예를 들어 기억하면, 일반 신용카드를 상위클래스 라고 하면, VIP(일반기능 + VIP 추가 기능) 신용카드를 하위 클래스라고 생각하면 됩니다.

상속관계와 포함관계

클래스 간의 관계를 설정할 때, 은근히 헤깔리는 개념입니다. 이는 다음과 같이 설정해줄 수 있습니다.

  • 상속관계 : is ~ a
  • 포함관계 : has ~ a

상속관계 예시는 '고래 - 포유류'가 있고, 포함관계 예시는 '자동차 - 바퀴' 정도가 있습니다.

다중 상속 불가

자바에서는 다중 상속을 허용하지 않습니다. 이는 클래스 간의 관계에 복잡성이 커지기 때문입니다.

final 클래스와 메서드

final 예약어를 클래스에 선언하게되면, 상속 시 오류가 발생합니다.

final 은 해당 클래스가 변하지 않는 클래스라고 선언을 해주는 것인데, 상속을 하게 되면, 메소드 오버라이딩으로 인해 기능의 수정이 발생할 수 있기 때문에 불가능합니다.

이러한 final 클래스를 사용하는 대표적인 예로 'String'이 있습니다. 이는 보안적인 문제로 외부에서 클래스의 메서드를 함부로 오버라이딩 하지 않도록 하기 위해서 주로 사용됩니다.

그렇다면, 범위를 좁혀서 일정 메서드만 오버라이딩이 불가능하도록 하는 방법은 없을까?

바로 이를 위해서 사용하는 것이 final 메서드 입니다.

final 메서드로 지정한 메서드는 상속 이후에도 하위 클래스에서 오버라이딩이 불가능합니다.

2. 오버라이딩

상위 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것을 의미합니다.

오버라이딩 조건

  • 선언부가 상위 클래스의 메서드와 일치
  • 접근 제어자를 상위 클래스의 메서드 보다 좁은 범위로 변경이 불가
  • 예외는 상위 클래스의 메서드 보다 많은 범위로 선언이 불가

접근제어자와 예외 관련 예시는 다음과 같습니다

class A

public class A {
    public void method(){
        System.out.println("무야호~!!");
    }
}

classB

public class B extends A{
    public void method(){
        System.out.println("오버라이딩~!!");
    }
}

위에서 public을 protected라고 바꾸면, 컴파일 에러가 발생하게 됩니다.

1710468935908

추가로 상위 클래스의 메서드에는 없는 예외를 추가하게 되면, 다음과 같이 컴파일 에러가 발생하게 됩니다.

1710469081708

super와 super()

상속하게 되면 상위 클래스의 참조값을 가져올 수 있는 예약어가 'super' 입니다.

일반적으로 객체 내부 생성자 및 메서드에서 상위 클래스의 멤버에 접근하게 될 때 사용합니다.

그리고 상위 클래스의 생성자를 호출하는 예약어가 'super()' 입니다.

💡

자바는 내부적으로 상속 관계에 있는 하위 클래스의 객체를 생성하면, 상위 클래스의 객체를 우선 생성한 뒤 그 다음에 생성하게 됩니다.

즉, 별도로 정의하지 않아도 하위 클래스의 기본 생성자 안에 super() 라는 예약어가 추가되어 있습니다.

이는 만약, 상위 클래스 내부에 생성자를 정의하여 기본 생성자가 없는 경우에 해당 생성자를 하위 클래스의 생성자 내부에 super() 예약어를 사용하여 추가해줘야 합니다.

다음 그림을 통해 실제 A 클래스에 추가된 생성자를 상속관계의 B 클래스에서 super(...) 예약어를 통해 호출하는 것을 알 수 있습니다.

1710469654048

3. 다형성

다형성은 상위 타입으로 선언된 부분에 다양한 하위 타입의 객체들을 사용하여 여러 형태로 구현이 가능한 성질을 의미합니다.

다형성의 핵심은 참조 타입의 형변환입니다. 해당 형변환에는 자동 형변환(Up Casting)과 강제 형변환(Down Casting)이 있습니다.

Up Casting

자동 형변환에 의해 다음과 같은 생성식이 가능합니다.

상위 클래스 / 인터페이스 타입_변수명 = new 하위클래스 생성자(...);

⚠️

다만, 주의할 점은 상위 타입의 변수로 하위 객체에 접근 시 상위 타입으로 부터 상속받거나 구현한 멤버에만 접근이 가능합니다

단, 오버라이딩 된 메서드는 힙메모리에 생성된 메서드의 참조값을 기준으로 결정되기 때문에 오버라이딩 된 내용이 호출됩니다.

즉, 메서드의 참조를 가져올 때는 하위 클래스 내부에 재정의 됐는지 여부 확인 후 상위 클래스의 메서드를 호출하는 순서가 적용된다는 것을 알 수 있습니다.

Down Casting

상위 타입 변수는 하위 타입 변수에 자동으로 형변환 되어 저장되지 않습니다.

하위 클래스 타입_변수명 = (하위 타입) 상위타입변수;

상위 타입 변수 라고 표현하는 이유는 하위 타입 객체가 상위타입으로 자동 형변환 된 후 다시 하위 타이븡로 변환되는 경우에만 Down Casting이 가능합니다.

다형성 사용

다형성은 메서드의 매개변수 또는 반환값으로 상위 클래스 타입으로 설정하여 하위 타입을 입력 받거나 반환하는 방식으로 사용됩니다.

/* 매개변수 */
public class A { ... }
public class B extends A{...}
public class C extends A{...}

public class Main{
	public static void main(String[] args){
		method(new A())); // 가능
		method(new B())); // 가능
		method(new C())); // 가능
	}

	public static void method(A a){
		...
	}
}
;/* 반환형 */
public class A { ... }
public class B extends A{...}
public class C extends A{...}

public class Worker{

	public A getA(){
		return new A();
	}

	public A getB(){
		return new B();
	}

	public A getC(){
		return new C();
	}
}

public class Main{
	public static void main(String[] args){
		Worker w = new Worker();
		A a = w.getA();
		A b = w.getB();
		A c = w.getC();
	}

}

InstanceOf

객체의 원래 클래스명을 체크하기 위한 예약어인 'instance of' 가 있습니다.

사용은 다음과 같습니다.

boolean 객체 instance of 클래스타입

// 다형성

class Parent { }
class Child extends Parent { }
class Brother extends Parent { }


public class Main {
    public static void main(String[] args) {

				Parent pc = new Child();  // 다형성 허용 (자식 -> 부모)

        Parent p = new Parent();

        System.out.println(p instanceof Object); // true 출력
        System.out.println(p instanceof Parent); // true 출력
        System.out.println(p instanceof Child);  // false 출력

        Parent c = new Child();

        System.out.println(c instanceof Object); // true 출력
        System.out.println(c instanceof Parent); // true 출력
        System.out.println(c instanceof Child);  // true 출력

    }
}

4. 추상 클래스

추상 클래스에는 abstract 예약어가 붙습니다.

추상클래스는 일반적으로 여러 개의 자식 클래스들에서 공통적인 필드나 메서드를 추출하여 만들 수 있습니다.

추상 클래스 안에서는 메서드를 선언만 하고, 내용은 구현하지 않습니다. 이를 추상 메서드 라고 합니다.

추상 클래스를 상속하는 구현 클래스에서 추상 메서드를 구현해줘야 합니다.

일반적으로, 설계의 목적으로 해당 클래스를 상속하는 하위 클래스에서 추상 메서드를 반드시 정의해야 함을 강제화 하기 위해서 사용합니다.

예를 들어 보겠습니다. 생물은 늙는다는 것은 진리입니다. 이는 모든 생물 객체에 적용해야 합니다. 따라서 사람 객체를 만들거나 동물 객체를 만들거나 상위 생물이라는 개념에 따라 나이라는 ' '공통 상태값' '과 늙는다는 ' 공통 행위 '가 생물이라면 주어져야 합니다. 이는 각 생물마다 다릅니다. 따라서 생물 객체마다 다르게 재정의하게 됩니다.

추상 메서드

abstract 예약어를 붙여 선언 가능합니다.

위에서 언급한 대로 추상 클래스 안에 정의된 추상 메서드는 구현체가 없고 중괄호({ })가 없습니다.

public abstract class 클래스명{
	abstract 리턴타입 메서드명(매개변수, ...);
}

추상 클래스 상속

추상 클래스는 일반 클래스와 동일하게 extends 예약어를 통해 상속되며, 반드시 추상 클래스에 선언된 추상 메서드를 구현(오버라이딩) 해줘야 합니다.

🧊 인터페이스

1. 인터페이스의 역할

상속관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야할 경우 인터페이스는 구현 클래스들에게 동일한 메서드를 제공해줍니다.

인터페이스에 정의된 메서드들은 추상 메서드 이며, 구현 클래스들은 강제적으로 구현해야 합니다.

이를 통해 구현 클래스들에게 동일한 사용 방법과 행위를 보장합니다.

인터페이스 선언

public interface 인터페이스명{
}

인터페이스는 클래스와 마찬가지로 default와 public만 선언이 가능합니다.

인터페이스 구성

인터페이스 멤버 구성

  • 모든 필드는 public static final이 자동으로 추가됩니다.(상수)
  • 모든 메서드는 public abstract 가 자동 추가됩니다.(추상 메서드)

인터페이스 구현

인터페이스는 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없기 때문에 클래스에 의해 구현되어 생성됩니다.

💡

인터페이스는 원래 인스턴스 생성이 불가능합니다. 하지만, 내부 추상 메서드들을 구현해주면, 객체처럼 생성이 가능합니다.

public class Test02 {
    public static void main(String[] args) {
        TestInterface testInterface = new TestInterface() {
            @Override
            public void testMethod() {
                System.out.println("귀여워 아주");
            }
        };

        testInterface.testMethod(); // result : 귀여워 아주
    }
}

인터페이스 상속

인터페이스 간에도 상속이 가능합니다. 이 때, 키워드는 클래스와 동일하게 extends 를 사용합니다.

클래스 상속과 인터페이스 구현은 동시에 사용 가능합니다.

인터페이스는 클래스와 다르게 다중 상속이 가능합니다.

public class Main implements C {

    @Override
    public void a() {
        System.out.println("A");
    }

    @Override
    public void b() {
				System.out.println("B");
    }
}

interface A {
    void a();
}
interface B {
    void b();
}
interface C extends A, B { }

2. 디폴트 메서드와 static 메서드

디폴트 메서드(default) 는 추상 메서드의 기본적인 구현을 제공하는 메서드입니다.

단, 디폴트 메서드는 인터페이스 내부에서 구현되어야 합니다.

public class Main implements A {

    @Override
    public void a() {
        System.out.println("A");
    }


    public static void main(String[] args) {
        Main main = new Main();
        main.a();

        // 디폴트 메서드 재정의 없이 바로 사용가능합니다.
        main.aa();
    }
}

interface A {
    void a();
    default void aa() {
        System.out.println("AA");
    }
}

인터페이스 내부에서 static 메서드 선언이 가능합니다.

정적 특성 그대로 인터페이스의 static 메서드 또한 객체 생성 없이 호출이 가능합니다.

선언과 호출 방법은 클래스의 static 메서드와 동일합니다.

public class Main implements A {

    @Override
    public void a() {
        System.out.println("A");
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.a();
        main.aa();
        System.out.println();

        // static 메서드 aaa() 호출
        A.aaa();
    }
}

interface A {
    void a();
    default void aa() {
        System.out.println("AA");
    }
    static void aaa() {
        System.out.println("static method");
    }
}

3. 다형성

인터페이스의 다형성도 클래스의 다형성과 동일합니다.

profile
백엔드 서버 엔지니어

0개의 댓글