포스코x코딩온 웹 풀스택 양성과정

[포스코x코딩온] 웹 풀스택 과정 16주차 회고 | JAVA 클래스 상속

Codult 2024. 4. 18. 23:26
728x90


📌 클래스의 상속

부모 클래스(상위 클래스)에서 정의된 필드와 메소드를 자식 클래스(하위 클래스)가 물려받는 것을 의미한다.

  • 중복 코드를 제거할 수 있다.
  • 클래스의 계층적 분류가 가능하다.
  • 클래스의 재사용과 확장성이 용이하다.

상속

  • extends 키워드를 사용하여 상속한다.
    ※ 클래스는 기본적으로 Object를 상속받는다. (extends Object가 생략된 상태)
  • super 키워드를 사용하여 상위클래스에 접근할 수 있다.
    super(): 하위 클래스에서 상위 클래스 생성자를 호출하는 데에 사용하며, 생성자의 가장 상단에 위치해야 한다.

📌 메소드 오버라이딩

상위클래스의 메소드를 하위클래스에서 재정의하는 것이다. (메소드의 이름, 매개변수 타입 및 갯수, 리턴 타입 등 모든 것을 동일하게 작성한다.)

  • super 키워드로 상위클래스에서 오버라이드 된 메서드를 호출할 수 있다.
  • @Override: annotation으로 메소드 오버라이딩을 컴파일러에 알려준다.

 

예시 - 상위클래스 Person, 하위클래스 Student

Person.java

public class Person {
	private String name;
    private int age;
    
    // 생성자
    public Person (String name, int age) {
    	this.name = name;
        this.age = age;
    }
    
    public String getName() {
    	return name;
    }
    public int getAge() {
    	return age;
    }
    
    // 메소드 오버라이딩: Object의 toString 메소드를 재정의
    @Override
    public String toString() {
    	return "Person/ name: " + name + ", age: " + age;
    }
}

Student.java

public class Student extends Person {
	private String major;
    
    public Student (String name, int age, String major) {
    	super(name, age);
        this.major = major;
    }
    
    @Override
    public String toString() {
    	return "Student/ super: " + super.toString() + ", major: " + major;
    }
}


📌 추상 클래스

추상 메소드를 갖는 클래스를 의미한다.

  • 추상 메소드: 선언은 되어 있으나, 구현이 되어 있지 않은 메소드이다.
  • 추상 메소드 없이도 추상 클래스를 만들 수 있다.
  • 추상 클래스는 인스턴스 생성하여 사용할 수 없다. (항상 상속되어야 사용가능하다.)
  • 추상 메소드가 있는 경우, 추상 클래스를 상속받는 하위 클래스에서 추상 메소드를 오버라이딩 해야한다.
    → 서브클래스마다 다른 구현이 필요한 메소드를 추상 메소드로 선언하여, 목적에 맞게 추상 메소드를 구현할 수 있다.

추상 클래스의 구현 - abstract 키워드 이용

  • 추상 메소드와 추상 클래스에 abstract 키워드를 붙여준다.
  • 추상 메소드는 선언만 한다.
abstract class Animal {
	// 추상 메소드 선언
	abstract public void sound();
    public void sleep() {
    	System.out.println("동물이 잠을 잔다.");
    }
}

class Dog extends Animal {
	@Override
    public void sound() {
    	System.out.println("멍멍);
    }
}

public class AbstractEx {
	public static void main (String[] args) {
    	Dog dog = new Dog();
        dog.sound();
        dog.sleep();
    }
}

 

📌 인터페이스

추상메소드의 모음으로, 하위 클래스가 같은 동작을 한다는 것을 보장하기 위해 사용한다.

  • 추상클래스와 달리, 인터페이스는 무조건 추상메소드가 있어야 한다.
  • 변수 선언이 불가능하며, 상수 선언만 가능하다.
  • 인터페이스를 상속받는 클래스는 인터페이스의 모든 추상메소드를 반드시 구현해야 한다.

인터페이스를 사용하는 이유

  1. 추상화
    : 구현이 필요한 메소드를 선언하여 상속받은 클래스에서 사용하게 한다.
  2. 다중 상속
    : 하나의 클래스는 여러 개의 인터페이스를 구현할 수 있다. (콤마(,)로 이어서 쓰면 됨)

인터페이스 사용

  • interface 키워드로 선언한다.
  • default 키워드를 이용하여 메소드를 구현할 수 있다.
interface Controller {
	void powerOn(); // public은 주로 생략한다.
    void powerOff();
    default void display() {
    	System.out.println("Display");
    };
}
  • implement 키워드를 이용하여 클래스에 상속할 수 있다.
class TV implements Controller {
	@Override
    public void powerOn() {
    	System.out.println("TV Power On");
    }
    @Override
    public void powerOff() {
    	System.out.println("TV Power Off");
    }
}

class Computer implements Controller {
	@Override
    public void powerOn() {
    	System.out.println("Computer Power On");
    }
    @Override
    public void powerOn() {
    	System.out.println("Computer Power Off");
    }
}
  • 실행 예시:
public class Electronic {
	public static void main(String[] args) {
    	TV tv = new TV();
        Computer computer = new Computer();
        tv.powerOn();	// TV Power On
        computer.powerOn();		// Computer Power On
        tv.display(); 	// Display
    }
}


📌 인터페이스와 추상클래스

하위 클래스에서 특정한 행동을 강제하기 위해 사용하나, 차이점이 있다.

1) 상속

  • 추상클래스: 하나의 클래스는 하나의 추상클래스만 상속받을 수 있다.
  • 인터페이스: 하나의 클래스는 여러 인터페이스를 구현 가능하다.

2) 멤버변수

  • 추상클래스: 멤버변수를 가질 수 있다.
  • 인터페이스: 변수 선언할 수 없으며, 상수는 포함할 수 있다.

3) 구성요소

  • 추상클래스: 멤버변수, 생성자, 추상메소드
  • 인터페이스: 상수 ,추상메소드, 디폴트메소드

4) 사용

  • 추상클래스: 상속 관계에서 공통의 기능이나 상태를 유지할 때 사용한다.
  • 인터페이스: 여러 클래스에서 공통으로 필요한 기능을 정의할 때 사용한다.

5) 생성자

  • 추상클래스: 생성자와 초기화 가능하다.
  • 인터페이스: 생성자와 초기화 불가능하다.


📌 다형성

하나의 인터페이스나 클래스를 여러 방식으로 동작하게 하는 것으로, 상속과 인터페이스를 활용하여 구현한다.

  • 다형성은 각 인터페이스나 클래스의 참조변수를 사용하여, 다양한 구현을 가진 객체를 참조한다.
// Animal을 상속받는 Dog, Cat 클래스가 있다고 가정

// dog는 Animal타입의 참조변수로, Dog 객체를 참조한다.
Animal dog = new Dog();
// cat은 Animal타입의 참조변수로, Cat 객체를 참조한다.
Animal cat = new Cat()

장점

  • 재사용성: 기존 코드를 재사용하여 새로운 클래스를 생성할 수 있다.
  • 확장성: 기존 코드를 수정하지 않고 기능을 추가하거나 확장할 수 있다.

단점

  • 복잡성 증가: 여러 객체가 동일한 인터페이스나 슈퍼클래스를 상속받을 때, 실제 어떤 객체의 메소드가 호출되는지 파악하기 어려울 수 있다.
  • 디버깅의 어려움: 여러 클래스가 동일한 메소드를 오버라이딩한 경우, 디버깅 시에 어떤 클래스의 메서드가 실제로 실행되는지 파악하기 어려울 수 있다.
  • 유지보수의 어려움: 다형성을 과도하게 사용하게 되면, 새로운 클래스나 메소드가 추가될 때 유지보수가 어려워질 수 있다.


📌 참고) final 키워드

  • 클래스에서 사용하는 경우: "상속될 수 없다"는 뜻
    (ex. final class Myclass{})
  • 메소드에서 사용하는 경우: "오버라이드 될 수 없다"는 뜻
  • 변수에서 사용하는 경우: "상수. 값 변경이 불가능하다"는 뜻
  • 매개변수에서 사용하는 경우: "매개변수 값 변경이 불가능하다"는 뜻
    (ex. void Func(final int numb){})
728x90