TIL 2023/10/31 Java extends interface

YEONGDO·2023년 10월 31일

8. 상속

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

상속의 사전적 정의는 부모가 자식에게 물려주는 행위

  • 객체 지향 프로그램에서도 부모 클래스의 필드와 메서드를 자식 클래스에게 물려줄 수 있다.
  • 상속을 사용하면 적은 양의 코드로 새로운 클래스를 작성할 수도 있고 공통적인 코드를 관리하여 코드의 추가와 변경이 쉬워질 수도 있다.
  • 이러한 특성 때문에 상속을 사용하면 코드의 중복이 제거되고 재사용성이 크게 증가하여 생산성과 유지보수성에 매우 유리해진다

1) 상속

  • 클래스간의 상속은 extends 키워드를 사용하여 정의할 수 있다.
public class 자식클래스 extends 부모클래스 {

}
  • 부모 클래스에 새로운 필드와 메서드가 추가되면 자식 클래스는 이를 상속받아 사용할 수 있다.
  • 자식 클래스에 새로운 필드와 메서드가 추가되어도 부모 클래스는 어떠한 영향도 받지 않는다.
  • 따라서 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.

2) 클래스간의 관계

  • 상속관계: 고래는 포유류다 (자식과 부모) / 힙합은 음악이다(ㅋㅋ맞나..)
  • 포함관계: 자동차는 타이어,차문,핸들을 가지고있다.

3) 단일 상속과 다중 상속
Java는 다중상속을 허용 XXXXXX

  • 다중상속을 허용하면 클래스간의 관계가 복잡해지는 문제가 생기기 때문

4) final 클래스와 final 메서드

public final class Car {}

...

public class SportsCar extends Car{} // 오류가 발생합니다.

클래스에 final 키워드를 지정하여 선언하면 상속할 수 없는 최종적인 클래스가 된다.

public class Car {
    public final void horn() {
        System.out.println("빵빵");
    }
}

...

public class SportsCar extends Car{
    public void horn() { // 오류가 발생합니다.
        super.horn();
    }
}

메서드에 final 키워드를 지정하여 선언하면 오버라이딩 할 수 없는 최종적인 메서드가 된다.

5) Object

Object 는 말그대로 “객체”를 의미하는 단어이며 보통, Object 클래스를 의미

  • Object 클래스는 Java 내 모든 클래스들의 최상위 부모 클래스
  • 따라서, 모든 클래스는 Object의 메서드를 사용할 수 있다.
  • 또한 부모 클래스가 없는 자식 클래스는 컴파일러에 의해 자동으로 Object 클래스를 상속받게 된다.
- Object **clone()** : 해당 객체의 복제본을 생성하여 반환함.
- boolean **equals(Object object)** : 해당 객체와 전달받은 객체가 같은지 여부를 반환함.
- Class **getClass()** : 해당 객체의 클래스 타입을 반환함.
- int **hashCode()** : 자바에서 객체를 식별하는 정수값인 해시 코드를 반환함.
- String **toString()** : 해당 객체의 정보를 문자열
로 반환함. & Object 클래스에서는 클래스이름 @해쉬코드값 리턴함.
- …

2. 오버라이딩과 super

1) 오버라이딩
부모 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것

  • 부모 클래스의 메서드를 그대로 사용 가능하지만 자식 클래스의 상황에 맞게 변경을 해야하는 경우 오버라이딩을 사용
  • 선언부가 부모 클래스의 메서드와 일치해야 한다.
  • 접근 제어자를 부모 클래스의 메서드 보다 좁은 범위로 변경할 수 없다.
  • 예외는 부모 클래스의 메서드 보다 많이 선언할 수 없다.

2) super
super는 부모 클래스의 멤버를 참조할 수 있는 키워드

  • 객체 내부 생성자 및 메서드에서 부모 클래스의 멤버에 접근하기 위해 사용될 수 있다.
  • 자식 클래스 내부에서 선언한 멤버와 부모 클래스에서 상속받은 멤버와 이름이 같을 경우 이를 구분하기 위해 사용
  • 자식 클래스의 메서드를 호출하면 super 키워드로 접근한 부모 클래스의 model, color 필드에 매개변수의 값이 저장
  • this 키워드로 접근한 자식 클래스의 price 필드에는 매개변수의 값이 저장

3) super()
super(…)는 부모 클래스의 생성자를 호출할 수 있는 키워드

  • 객체 내부 생성자 및 메서드에서 해당 객체의 부모 클래스의 생성자를 호출하기 위해 사용될 수 있다.
  • 자식 클래스의 객체가 생성될 때 부모 클래스들이 모두 합쳐져서 하나의 인스턴스가 생성된다.
  • 이 때 부모 클래스의 멤버들의 초기화 작업이 먼저 수행이 되어야한다.

3. 다형성

1) 참조변수의 타입변환

@ 자동 타입변환

  • 부모타입 변수 = 자식타입객체; 는 자동으로 부모타입으로 변환이 일어난다.
  • 자식 객체는 부모 객체의 멤버를 상속받기 때문에 부모와 동일하게 취급될 수 있다.
  • 주의할 점은 부모타입 변수로 자식객체의 멤버에 접근할 때는 부모 클래스에 선언된 즉, 상속받은 멤버만 접근할 수 있다.

@ 강제 타입변환

  • 부모타입객체는 자식타입 변수로 자동으로 타입변환 XXX
  • (자식타입) 즉, 타입변환 연산자를 사용하여 강제로 자식타입으로 변환할 수 있다.
    // 자식타입객체가 자동 타입변환된 부모타입의 변수
Mammal mammal = new Whale();
mammal.feeding();

// 자식객체 고래의 수영 기능을 사용하고 싶다면
// 다시 자식타입으로 강제 타입변환을 하면된다.
Whale whale = (Whale) mammal;
whale.swimming();

2) 다형성이란?
다형성이란 ‘여러 가지 형태를 가질 수 있는 능력’을 의미

Tire tire = new HankookTire("HANKOOK");
Tire tire = new KiaTire("KIA");
  • 부모타이어 변수 = 자식타이어객체; 를 선언하여 자동 타입변환된 변수를 사용하여 각각의 자식타이어 객체에 재정의 된 메서드를 통해 다양한 승차감을 가진 자동차를 생성할 수 있다.
public Car(Tire tire) {
    this.tire = tire;
}

...

Car car1 = new Car(new KiaTire("KIA"));
Car car2 = new Car(new HankookTire("HANKOOK"));
  • 매개변수에도 다형성이 적용될 수 있다.
  • Car 생성자에서 매개변수의 타입이 부모 타이어 이기 때문에 자식 타이어 객체들을 매개값으로 전달할 수 있다.
Tire getHankookTire() {
    return new HankookTire("HANKOOK");
}

Tire getKiaTire() {
    return new KiaTire("KIA");
}

...

Tire hankookTire = car1.getHankookTire();
KiaTire kiaTire = (KiaTire) car2.getKiaTire();
  • 반환타입에도 다형성이 적용될 수 있다.
  • 반환타입이 부모 타이어 이기 때문에 자식 타이어 객체들을 반환값으로 지정할 수 있다.
  • 또한 자동 타입변환이된 반환값인 자식 타이어 객체를 강제 타입변환할 수도 있다.

3) instanceof
다형성 기능으로 인해 해당 클래스 객체의 원래 클래스명을 체크하는것이 필요한데 이때 사용할 수 있는 명령어가 instance of

  • 이 명령어를 통해서 해당 객체가 내가 의도하는 클래스의 객체인지 확인할 수 있다.
  • {대상 객체} instance of {클래스 이름} 와 같은 형태로 사용하면 응답값은 boolean.

4. 추상 클래스

1) 추상 클래스
클래스가 설계도라면 추상 클래스는 미완성된 설계도이다.
abstract 키워드를 사용하여 추상 클래스 선언

public abstract class 추상클래스명 {

}
  • 추상 클래스는 추상 메서드를 포함할 수 있다.
    • 추상 메서드가 없어도 추상 클래스로 선언할 수 있다.
  • 추상 클래스는 자식 클래스에 상속되어 자식 클래스에 의해서만 완성될 수 있다.
  • 추상 클래스는 여러개의 자식 클래스들에서 공통적인 필드나 메서드를 추출해서 만들 수 있다.

2) 추상 메서드
추상 메서드는 아직 구현되지 않은 미완성된 메서드이다.
abstract 키워드를 사용하여 추상 메서드를 선언

public abstract class 추상클래스명 {
		abstract 리턴타입 메서드이름(매개변수, ...);
}
  • 추상 메서드는 일반적인 메서드와는 다르게 블록{ }이 없다.
  • 즉, 정의만 할 뿐, 실행 내용은 가지고 있지 않다.

3) 추상 클래스 상속
추상 메서드는 extends 키워드를 사용하여 클래스에서 상속

public class 클래스명 extends 추상클래스명 {
		@Override
    public 리턴타입 메서드이름(매개변수, ...) {
		       // 실행문
    }
}
  • 상속받은 클래스에서 추상 클래스의 추상 메서드는 반드시 오버라이딩 되어야한다.

9. 인터페이스

1. 인터페이스의 역할

  • 두 객체를 연결해주는 다리 역할
  • 상속 관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장해줄 수 있다.
  • 스펙이 정의된 메서드들의 집합
  • 구현 클래스들은 반드시 정의된 메서드들을 구현해야한다.
  • 동일한 사용 방법과 행위를 보장
  • 다형성 적용 가능

1) 인터페이스 선언

public interface 인터페이스명 { 

}
  • 인터페이스는 클래스와 마찬가지로 public, default 접근 제어자를 지정할 수 있다.

2) 인터페이스 구성

public interface 인터페이스명 { 
		public static final char A = 'A';
    static char B = 'B';
    final char C = 'C';
    char D = 'D';

    void turnOn(); // public abstract void turnOn();
}
  • 모든 멤버 변수는 public static final > 생략 가능
  • 모든 메서드는 pubilc abstract > 생략 가능(static, default 메서드 제외)
  • 생략되는 제어자는 컴파일러가 자동으로 추가

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

public class 클래스명 implements 인터페이스명 { 
			// 추상 메서드 오버라이딩
			@Override
	    public 리턴타입 메서드이름(매개변수, ...) {
			       // 실행문
	    }
}
  • 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야 한다.
  • 만약 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경해주면 된다.

4) 인터페이스 상속

  • 클래스와 다르게 다중 상속 가능
  • implements XX / extends OO
  • 인터페이스 구현은 상속과 함께 사용 가능

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

1) 디폴트 메서드

  • 추상 메서드의 기본적인 구현을 제공하는 메서드
  • 메서드 앞에 default 키워드를 붙이며 블럭{ }이 존재해야한다.
  • default 메서드 역시 접근 제어자가 public 이며 생략이 가능
  • 추상 메서드가 아니기 때문에 인터페이스의 구현체들에서 필수로 재정의 할 필요는 없다.

2) static 메서드

  • 인터페이스에서 static 메서드 선언 가능
  • static 특성 그대로 인터페이스의 static 메서드 또한 객체 없이 호출 가능
  • 선언하는 방법과 호출하는 방법은 클래스의 static 메서드와 동일
    (접근 제어자를 생략하면 컴파일러가 public을 추가)

3. 다형성

1) 타입변환

@ 자동타입변환

인터페이스 변수 = 구현 객체; // 자동타입변환

public class Main {
    public static void main(String[] args) {
        
        // A 인터페이스에 구현체 B 대입
        A a1 = new B();
        
        // A 인터페이스에 구편체 B를 상속받은 C 대입
        A a2 = new C();
        
    }
}

interface A { }
class B implements A {}
class C extends B {}

@ 강제타입변환

인터페이스 타입을 자동 타입 변환 후, 다시 구현 클래스 타입으로 변환

  • 필요성: 구현 클래스 타입에 선언된 다른 멤버를 사용하기 위해

2) 인터페이스의 다형성

  • 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
  • 인터페이스는 메서드의 매개변수의 타입으로 사용될 수 있다.
profile
개발 블로그

0개의 댓글