Java

[Java] 10. Java에서의 상속

Joo.v7 2024. 8. 30. 21:37

Chapter 1: 서브 클래스 작성

  • 클래스 확장
  • 슈퍼 클래스 멤버에 엑세스
  • 슈퍼 클래스 생성자 호출

Chapter2: 메소드 구현

  • 가상 메소드(Virtual Method)
  • 메소드 오버라이딩
  • final 메소드
  • final 클래스

Chapter 3: 인터페이스

  • 인터페이스 선언
  • 다중 인터페이스 구현
  • 인터페이스 메소드 구현
  • 인터페이스의 default 메소드
  • 인터페이스의 static 메소드 구현

Chapter 4: 추상 클래스

  • 추상 클래스 선언
  • 클래스 계층 구조에서 추상 클래스 사용
  • 클래스 계층 구조에서 추상 클래스 사용(2)
  • 추상클래스와 인터페이스
  • 추상 메소드 구현

Lab 10-1: 상속을 사용하여 인터페이스 구현


Chapter 1: 서브 클래스 작성

클래스 확장

  • 한 클래스(Super class)에서 파생되는 클래스(Sub class)를 생성.
    • 파생되는 Sub class는 Super class의 대부분의 요소를 상속. (생성자/소멸자 제외)
    • 파생되는 Sub class는 Super class보다 더 공개적이다.
      ex) Super class가 protected로 선언되면, Sub class는 public/protected는 가능하지만 default/private 불가능.
class Product { // Product -> Super class
    ...
}

class IDCard extends Product { // IDCard -> Sub class
    ...
}

 

슈퍼 클래스 멤버에 엑세스

  • protected 멤버는 암시적으로 파생된 클래스에 상속.
  • 파생된 클래스의 메소드에서 상속된 protected 멤버에 access 할 수 있다.
    (같은 패키지 or 상속관계access가능, 다른 패키지 or 상속 관계가 아닌 클래스private 멤버처럼 동작)
/* 같은 패키지 or 상속 관계에 있는 클래스 access O */
class SuperClass {
    protected String name;
}

class SubClass extends SuperClass {

}

class FurtherSubClass extends SubClass {
    void success() {
        System.out.println(name); // Okay
    }
}

/* 다른 패키지 or 상속 관계가 없는 클래스 access X */
class Product {
    ...
    protected String name;
}

class IDCard extends Product {
    ...
    public String getTitle() {
        return name;
    }
}

class External {
    void access(Product product) {
        product.name;	// 컴파일시 오류
    }
}

 

슈퍼 클래스 생성자 호출

  • Super class 생성자는 반드시 super 키워드를 사용해서 호출.
  • private 생성자는 sub class에서 access 할 수 없다.
  • super 키워드로 식별자 범위 한정. ex) super.name
class Product {
    protected Product(String name)
    ...
}

class IDCard extends Product {
    public IDCard(String name) {
        super(name);
    }
}

 

 


 

Chapter2: 메소드 구현

 Sub class에서 Super class의 method를 재정의할 수 있다. -> override(오버라이딩)

가상 메소드(Virtual Method)

  • 클래스 계층구조 내에서 같은 시그니처로 하위 클래스에서 재정의(오버라이딩) 할 수 있는 메소드.
  • 객체지향 프로그램밍의 다형성에서 아주 중요한 부분.
  • Java의 모든 메소드는 가상 메소드로 동작. (virtual 메소드만 override 가능)

 

메소드 오버라이딩

  • Sub class에서 Super class의 메소드와 같은 시그니처로 정의.
  • Sub class에서 오버라이드하는 메소드는 Super class의 가상 메소드보다 낮은 가시성을 가질 수 없다.
    (super class에서 public으로 선언됐으면, sub class에서 public보다 낮은 default, priavte ... 불가능하다)
class Product {
    public String getName() {
        return "Product";
    }
}

public class IDCard extends Product {
    public String getName() {
        return "IDCard";
    }
}
Product product = new Product();
IDCard iDCard = new IDCard();
System.out.println(product.getName());	// “Product”
System.out.println(iDCard.getName());	// “IDCard”

 

final 메소드

  • final 로 선언된 메소드는 Sub class에서 Override 할 수 없다.
/* final로 선언된 메소드 상속 불가. */
class Product {
    final String getName() {
        return "Product";
    }
}

class IDCard extends Product {
    public String getName() {   // 컴파일 시 오류
        return "IDCard";
    }
}

/* super class의 final로 선언된 메소드를 sub class는 상속. */
class Product {
    final String getName() {
        return "Product";
    }
}

class IDCard extends Product {
}

IDCard idCard = new IDCard();
idCard.getName();	// “Product”

 

final 클래스

  • Super class로 동작되도록 설계하지 않은 클래스를 파생하는 것은 위험.
  • final로 선언된 클래스에서는 sub class를 파생할 수 없다.
  • Java의 String, StringBuilder 등의 클래스는 final로 선언됨.
/* final Class는 상속 불가 */
public final class Product {
    final String getName() {
        return "Product";
    }
}

class IDCard extends Product {	// 컴파일시 오류

}

 

 


 

Chapter 3: 인터페이스

인터페이스 선언

  • interface 키워드를 사용하여 선언.
  • interface는 선언만 하고, 구현(implements)은 sub에서 한다.
/* interface 예시 */
interface BinaryOp { // interface: 선언만 한다.
    void apply(int right, int left);
}

class Adder implements BinaryOp { // 구현 한다.
    public int apply(int right, int left) {
        ...
    }
}

 

다중 인터페이스  메소드 구현

  • 클래스는 하나 이상의 interface를 구현(implements)할 수 있다. (Java는 단일 상속만 허용하지만 예외다.)
  • 인터페이스는 하나 이상의 인터페이스를 확장할 수 있다.
  • 클래스는 interface의 모든 추상 메소드(구현되지 않은 메소드)를 반드시 구현해야 한다.
interface Flyable {
    void fly();
}

interface Sucklable {
    void suckle();
}

class Bat implements Flyable, Sucklable { // 2개의 interface를 implements함.
    void fly() { ... }
    void suckle() { ... }
}

 

인터페이스 메소드 구현

  • 구현되는 메소드는 interface의 메소드와 시그니처가 같아야 한다.
  • 구현 메소드 역시 가상 메소드로, 파생되는 클래스에서 오버라이드 할 수 없다.
  • 클래스에서 구현되는 메소드는 interface의 추상 메소드(구현되지 않은 메소드)보다 좁은 가시성을 가질 수 없다.
    (클래스가 interface의 메소드를 구현할 때, access level을 interface에 선언된 메소드보다 좁게 설정할 수 없다)
// 접근 제한은 interface보다 좁게 X, return 타입/이름/파라미터 목록이 같아야 한다.
interface Attackable {
    int Attack(int power, String targetName);
}

class Tank implements Attackable {
    public int Attack(int power, String targetName) { … }
    public int Attack(String targetName, int power) { … }	// 컴파일시 오류
}

 

인터페이스의 default 메소드

  • Java에서 type을 정의하는 4가지: class, abstract class, interface, enum
  • default 메소드는 interface에 기본 구현을 제공.

설명) 

더보기

interface에 새로운 메소드를 추가해야 할 때, 기존에 해당 interface를 implements한 모든 클래스들이 해당 메소드를 구현해야하는 문제가 발생함. (interface를 implements 하려면 interface에서 요구하는 모든 메소드를 구현해야함)

 

이를 해결하기 위해, default method는 interface에 기본 구현을 제공할 수 있게 한다. 이를 통해 새로운 메소드를 interface에 추가하면서도, 기존의 서브타입들에게 영향을 주지 않는다.

interface InterfaceWithDefaultMethod {
    void aMethod(); // 일반 추상 메소드
    void default Method() { ... } // default 메소드
}
  • default 메소드: 두 가지 패턴을 사용하여 활용 가능.
    • Optional Method 패턴: interface에 메소드를 정의하되, implements가 이를 선택적으로 구현할 수 있게함.
      (기존 interface에 새로운 메소드를 추가하면서 기존 implements에 미치는 영향을 최소화 ex. Iterator)
    • Multiple Inheritance of Behavior: interface가 다양한 default method를 제공.
      (여러 interface에서 제공하는 default method들을 조합하여 사용.)

 

인터페이스의 static 메소드 구현

  • interface에서 static 메소드를 구현 후 사용. (클래스와 방법은 동일)
  • 호출시 메소드 전체 이름을 사용.
/* interface에서 static method 정의 */
public interface MathUtils {
    // 정적 메소드 정의
    static int add(int a, int b) {
        return a + b;
    }

    static int subtract(int a, int b) {
        return a - b;
    }
}

/* MathUtils interface의 instance 없이 호출 가능 */
public class Main {
    public static void main(String[] args) {
        // 인터페이스의 정적 메소드 호출
        int sum = MathUtils.add(5, 3);
        int difference = MathUtils.subtract(5, 3);

        System.out.println("Sum: " + sum); // 출력: Sum: 8
        System.out.println("Difference: " + difference); // 출력: Difference: 2
    }
}

 

 


 

Chapter 4: 추상 클래스

 공통적인 메소드나 데이터들을 구현해서 파생하는 클래스.

추상 클래스 선언

  • 클래스 instance를 생성할 수 없다.
  • 선언이 아닌 구현(코드)을 상속하는 형태로 사용.
  • abstract 키워드를 사용해서 선언.
/* abstract class는 instance를 생성할 수 없다 */
abstract class Factory {
    ...
}

class Test {
    public static void main(String[] args) {
    Factory factory = new Factory(); 	// 컴파일시 오류
    }
}

 

클래스 계층 구조에서 추상 클래스 사용

 

interface의 추상 메소드인 move를 추상 클래스인 Unit에서 구현해서, sub class인 Tank가 move()를 오버라이드 할 수 있다.

interface Moveable {
    void move(int x, int y);
}

public abstract class Unit implements Moveable {
    protected String name;
    protected int x;
    protected int y;

    public String getName() {
        return this.name;
    }

    public void move(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Tank extends Unit {
    public void move(int x, int y) {
        System.out.println("Now Moving");
    }
}

 

클래스 계층 구조에서 추상 클래스 사용(2)

 

추상 클래스는 StringInstrument는 interface를 구현하지 않음. 그래서 sub class가 interface의 추상 메소드들을 구현해야함.

interface Instrument {
    void play();
    void tune();
}

public abstract class StringInstrument {
    String name;
    int numberOfString;
}

public class Guitar extends StringInstrument implements Instrument {

    public void play() {}
    public void tune() {}
}

 

추상클래스와 인터페이스

  • 유사점
    • instance화 될 수 없다: 객체로 생성되어 사용할 수 없다.
    • final로 선언될 수 없다: interface와 추상 클래스는 파생되기 위한 용도이기 때문이다.
  • 차이점
    • interface는 같은 타입으로서의 구현을 강제.
    • 추상 클래스는 데이터와 구현(코드)를 상속하고 확장을 유도.
    • interface는 interface 외에는 확장할 수 없다.

 

추상 메소드 구현

  • 추상 클래스(abstract class)에 추상 메소드(구현이 없는 메소드) 선언.
  • abstract 키워드 사용
  • 추상 클래스에서만 추상 메소드를 선언할 수 있다.
  • 추상 메소드는 구현을 포함할 수 없다.
public abstract class StringInstrument {
    String name;
    int numberOfString;

// 추상 메소드 선언, 구현은 포함될 수 없다.
    public abstract void changeString(); 
}

public class Guitar extends StringInstrument {
// sub class는 super class의 추상 메소드를 반드시 구현해야함.
    public void changeString() { ... } 
}

 

Lab 10-1: 상속을 사용하여 인터페이스 구현

2024.08.31 - [코딩 문제] - [연습 문제] 상속을 사용하여 인터페이스 구현

 

[연습 문제] 상속을 사용하여 인터페이스 구현

Adder와 Multiplier는 두 숫자를 인자로 받아 연산하는 같은 행위(Common Behavior)를 수행한다. 이 공통된 행위를 interface로 만든다.(BinaryOp) 그 후 Adder와 Multiplier는 이를 implements 한다. 코드BinaryOp.javainterf

lightningtech.tistory.com

 

 


 

 

출처: https://github.com/gikpreet/class-programming_with_java/tree/master

 

GitHub - gikpreet/class-programming_with_java

Contribute to gikpreet/class-programming_with_java development by creating an account on GitHub.

github.com

'Java' 카테고리의 다른 글

[Java] 11. Generics(제네릭)  (0) 2024.08.31
[Java] Singleton 패턴  (0) 2024.08.31
[Java] 09. 객체 생성과 제거  (0) 2024.08.30
[Java] Getter / Setter 메소드  (0) 2024.08.28
[Java] 08. 참조 타입  (2) 2024.08.28