20230406 TIL

박우현 (Joshua)·2023년 4월 7일

항해99_TIL

목록 보기
4/20

20230406 TIL
오늘은 자바 상속과 인터페이스에 관해서 배웠다.

📋 상속


📌 클래스간의 관계와 상속

부모 클래스의 필드와 메서드를 자식 클래스에게 물려준다.
상속을 사용하면 코드의 중복이 제거되고 재사용성이 크게 증가하여 생산성과 유지보수성에 좋다.

상속
extends 키워드를 사용하여 정의

public class 자식클래스 extends 부모클래스 {}


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

클래스간의 관계

  • 상속관계 : is - a (”~은 ~(이)다”)
  • 포함관계 : has - a (”~은 ~을(를) 가지고 있다”)
    포함관계는 우리가 일전에 봤던 자동차와 타이어, 차문, 핸들 과의 관계라고 볼 수 있습니다.
    • 자동차는 타이어를 가지고 있다.
    • 자동차는 차문을 가지고 있다.
    • 자동차는 핸들을 가지고 있다.

단일 상속과 다중 상속
Java는 다중상속을 허용하지 않습니다.

final 클래스와 final 메서드

  • final 클래스 : 클래스에 final 키워드를 지정하여 선언하면 최종적인 클래스가 됨으로 더 이상 상속할 수 없는 클래스가 된다.
  • final 메서드 : 메서드에 final 키워드를 지정하여 선언하면 최종적인 메서드가 됨으로 더 이상 오버라이딩할 수 없는 메서드가 된다.

Object

  • Object 클래스 = Java 내 모든 클래스들의 최상위 부모 클래스
  • 모든 클래스는 Object의 메서드를 사용할 수 있다.
  • 부모 클래스가 없는 자식 클래스는 컴파일러에 의해 자동으로 Object 클래스를 상속받는다.

📌 오버라이딩

부모 클래스로부터 상속받은 메서드의 내용을 재정의 하는 것 @Override 를 메소드 전에 선언해 사용.
1. 선언부가 부모 클래스의 메서드와 일치해야 한다.
2. 접근 제어자를 부모 클래스의 메서드 보다 좁은 범위로 변경할 수 없다.
3. 예외는 부모 클래스의 메서드 보다 많이 선언할 수 없다.

예제

//부모 클래스
public class Car {
	...
    public double brakePedal() {
        speed = 0;
        return speed;
    }
    public void horn() {
        System.out.println("빵빵");
    }
    ...
}
// 자녀 클래스
public class SportsCar extends Car{
    ...
    @Override	//오버라이딩
    public double brakePedal() {
        speed = 100;
        System.out.println("스포츠카에 브레이크란 없다");
        return speed;
    }
    @Override	//오버라이딩
    public void horn() {
        booster();
    }
    ...
}

super 와 super()

  • super
    부모 클래스의 멤버를 참조할 수 있는 키워드
    객체 내부 생성자 및 메서드에서 부모 클래스의 멤버에 접근하기 위해 사용된다.
    자식 클래스 내부에서 선언한 멤버와 부모 클래스에서 상속받은 멤버와 이름이 같을 경우 이를 구분하기 위해 사용
public void setCarInfo(String model, String color, double price) {
    super.model = model; // model은 부모 필드에 set
    super.color = color; // color는 부모 필드에 set
    this.price = price; // price는 자식 필드에 set
}
  • super()
    부모 클래스의 생성자를 호출할 수 있는 키워드
    객체 내부 생성자 및 메서드에서 해당 객체의 부모 클래스의 생성자를 호출하기 위해 사용
    1. 부모 클래스의 멤버들의 초기화 작업이 먼저 수행된다.
    2. 따라서 자식 클래스의 생성자에서는 부모 클래스의 생성자가 호출됩니다. 따로 표시를 안해도 컴파일러가 super(); 를 자식 클래스 생성자 첫 줄에 자동으로 추가
    3. 또한 부모 클래스의 생성자는 가장 첫 줄에서 호출이 되어야합니다.

📌 다형성

참조변수의 타입변환

  • 자동 타입변환
    부모타입 변수 = 자식타입객체; 는 자동으로 부모타입으로 변환이 된다.
    다만, 부모타입 변수로 자식객체의 멤버에 접근할 때는 부모 클래스에 선언된 즉, 상속받은 멤버만 접근할 수 있다.
  • 강제 타입변환
    부모타입객체는 자식타입 변수로 자동으로 타입 변환되지 않아서 타입변환 연산자를 사용한다. 자식타입 변수 = (자식타입) 부모타입객체;
    다만, 자식타입객체가 부모타입으로 자동 타입변환된 후 다시 자식타입으로 변환될 때 만 강제 타입변환이 가능하다.
    부모타입 변수로는 자식타입객체의 고유한 멤버를 사용할 수 없기 때문에 사용이 필요한 경우가 생겼을 때 사용한다.
public class Main {
    public static void main(String[] args) {
        // 자동 타입변환된 부모타입의 변수의 자식 객체
        Mammal mammal = new Whale();
        mammal.feeding(); //상속된 기능
        // 자식객체 고래의 수영 기능을 사용하고 싶다면
        // 다시 자식타입으로 강제 타입변환을 하면된다.
        Whale whale = (Whale) mammal;
        whale.swimming(); //자식객체의 기능
        // 자동 타입변환이 되지 않은 부모타입의 변수를 자식 타입으로 바꾸러하면 에러
        Mammal newMammal = new Mammal();
        // Whale newWhale = (Whale) newMammal; 
    }
}

다형성이란?
'여러 가지 형태를 가질 수 있는 능력’을 의미
참조변수 타입변환을 활용해서 다형성을 구현
매개변수, 반환타입 에도 다형성이 적용될 수 있습니다.

//매개변수
Car car1 = new Car(new KiaTire("KIA"));
Car car2 = new Car(new HankookTire("HANKOOK"));
//반환 타입 
Tire getHankookTire() {
    return new HankookTire("HANKOOK");
}

instance of
다형성 기능으로 인해 해당 클래스 객체의 원래 클래스명을 체크하는것이 필요한데 이때 사용할 수 있는 명령어
{대상 객체} instance of {클래스 이름} 와 같은 형태로 사용하면 응답값은 boolean

// 다형성
//클래스
class Parent { }
class Child extends Parent { }
//메인 클래스
public class Main {
    public static void main(String[] args) {
        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 출력
    }
}

📌 추상 클래스

추상 클래스란?
미완성된 설계도

  • abstract 키워드를 사용하여 추상 클래스를 선언
    public abstract class 추상클래스명 {    }
  • 추상 클래스는 추상 메서드를 포함할 수 있다.
    • 추상 메서드가 없어도 추상 클래스로 선언할 수 있습니다.
  • 추상 클래스는 자식 클래스에 상속되어 자식 클래스에 의해서만 완성된다.
  • 추상 클래스는 여러개의 자식 클래스들에서 공통적인 필드나 메서드를 추출해서 만든다.

추상 메서드
아직 구현되지 않은 미완성된 메서드

  • abstract 키워드를 사용하여 추상 메서드를 선언
public abstract class 추상클래스명 {
		abstract 리턴타입 메서드이름(매개변수, ...);
}
  • 추상 메서드는 일반적인 메서드와는 다르게 블록{ }이 없다.
    • 정의만 할 뿐, 실행 내용 X

추상 클래스 상속
추상 메서드는 extends 키워드를 사용하여 클래스에서 상속된다.

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

📋 인터페이스


📌 인터페이스의 역할

두 객체를 연결해주는 다리 역할을 한다.
상속 관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장 해준다.

인터페이스 선언
interface 키워드를 사용하여 인터페이스를 선언할 수 있습니다.

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

인터페이스 구성
(생략되는 제어자는 컴파일러가 자동으로 추가.)

  • 모든 멤버변수는 public static final 이어야합니다.(생략 가능)
  • 모든 메서드는 public abstract 이어야합니다. (생략 가능) (static 메서드와 default 메서드 예외)

인터페이스 구현
implements 키워드를 사용하여 인터페이스를 구현
인터페이스는 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없기 때문에 클래스에 구현되어 생성된다.

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

인터페이스 상속
인터페이스간의 상속이 가능합니다.

  • 인터페이스간의 상속은 implements 가 아니라 extends 키워드를 사용합니다.
  • 인터페이스는 클래스와는 다르게 다중 상속이 가능합니다.
  • 인터페이스의 구현은 상속과 함께 사용될 수 있다.
public class Main extends D implements C {
    @Override
    public void a() {
        System.out.println("A");
    }
    @Override
    public void b() {
        System.out.println("B");
    }
    @Override
    void d() {
        super.d();
    }
    public static void main(String[] args) {
        Main main = new Main();
        main.a();
        main.b();
        main.d();
    }
}
//인터페이스
interface A {
    void a();
}
interface B {
    void b();
}
//여러 인터페이스 상속
interface C extends A, B {
}
//부모 클래스
class D {
    void d() {
        System.out.println("D");
    }
}

📌 디폴트 메서드와 static 메서드

디폴트 메서드
추상 메서드의 기본적인 구현을 제공하는 메서드

  • 메서드 앞에 default 키워드를 붙이며 블럭{ }이 존재해야한다.
  • default 메서드 역시 접근 제어자가 public (생략 가능)
  • 추상 메서드가 아니기 때문에 인터페이스의 구현체들에서 필수로 재정의 할 필요 X
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을 추가한다.
public class Main implements A {
	...
    public static void main(String[] args) {
        ...
        // static 메서드 aaa() 호출
        A.aaa();
    }
}
interface A {
	...
    static void aaa() {
        System.out.println("static method");
    }
}

📌 다형성

타입 변환

  • 자동 타입변환
    인터페이스 변수 = 구현객체;
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 {}
  • 강제 타입변환
    구현객체타입 변수 = (구현객체타입) 인터페이스변수;
public class Main {
    public static void main(String[] args) {
        // A 인터페이스에 구현체 B 대입
        A a1 = new B();
        a1.a();
        // a1.b(); // 불가능
        System.out.println("\nB 강제 타입변환");
        B b = (B) a1;
        b.a();
        b.b(); // 강제 타입변환으로 사용 가능
    }
}
...
interface A {
    void a();
}
class B implements A {
    @Override
    public void a() {
        System.out.println("B.a()");
    }
    public void b() {
        System.out.println("B.b()");
    }
}

인터페이스의 다형성
사용 방법은 동일하지만 다양한 특징과 결과를 가질 수 있는 것

// LG TV 구현체를 조작
MultiRemoteController mrc = new LgTv("LG");
mrc.turnOnOff();
mrc.volumeUp();
// 조작 대상을 Samsung TV로 교체
mrc = new SamsungTv("Samsung");
mrc.turnOnOff();
mrc.channelUp();

위 처럼, TV구현객체를 교체해도 멀티리모컨인터페이스 변수는 전혀 수정작업 없이 그대로 기능을 호출할 수 있다. 또한, 멀티리모컨으로 티비를 사용하는 방법은 동일하지만 어떤 TV구현객체가 대입되었느냐에 따라 실행 결과가 다르게 나온다.


📓 오늘 느낀 점 한줄 요약

"I am your father" 🛸

20230406 TIL

profile
매일 매일 성장하자

0개의 댓글