상속과 추상화

박상윤·2022년 8월 17일

📌 상속이란?

기존 클래스를 확장하여 새로운 클래스를 작성하는 기법

  • 새로운 클래스 뒤에 extends와 확장할 클래스를 적어서 표현
  • 자식 클래스는 부모 클래스의 모든 멤버를 상속(생성자, 초기화 블록 제외)
  • 같은 내용의 코드를 한곳에서 관리함으로써 코드의 중복이 줄어듦
  • 단일 상속만 가능

📍 클래스 간 관계

상속 관계 : is - a 관계

  • 개발자는 사람이다
  • 사람 : 부모 클래스 - 개발자 : 자식클래스

포함 관계 : has - a 관계

  • 한 클래스를 다른 클래스의 참조변수로 사용하는 경우

📍 오버라이딩(overriding)이란?

부모클래스로부터 상속받은 메서드의 내용을 변경하는 것

  • 이름,매개변수,반환 타입이 같아야 함
  • 접근제어자를 부모보다 좁은 범위로 할 수 없음
  • 부모 클래스의 메서드보다 많은 수의 예외를 선언할 수 없음
  • 인스턴스 메서드를 static 제어자로 바꿀 수 없음

📍 오버라이딩 vs 오버로딩

오버 라이딩 : 부모 클래스에 존재하는 메서드를 자식 클래스에서 재정의 하는것

오버 로딩 : 같은 함수의 이름으로 매개변수를 달리하여 새롭게 만드는 것(new)

📍 super와 super()

super

  • 자식 클래스에서 부모 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수
    super()
  • Object 클래스를 제외하고 모든 클래스에는 this()혹은 super()를 호출해야 함
  • 상속받는 클래스에서 생성자가 없으면 컴파일러가 자동으로 추가
  • 상속받는 클래스에서 생성자가 있으면 명시적으로 super()를 호출해야함

📍 다형성

여러가지 형태를 가질 수 있는 성질

  • 부모클래스 타입의 참조변수로 자식클래스의 인스턴스를 참조 가능
package Chapter7;

class Animal {
    
}

class Dog extends Animal {
    
    
} // Animal 상속

public class PolymorphismExample {
    public static void main(String[] args) {
        Animal dog = new Dog();
      	// 부모클래스 타입의 참조 변수 : Animal
        // 자식클래스 인스턴스 참조
    }
}

📍 참조 변수와 다형성

  • 서로 상속관계에 있는 클래스 사이에서만 형변환이 가능
  • 메서드 매개변수가 같은 부모 클래스라면 부모 클래스 타입을 매개변수 타입으로 선언 가능
  • 배열 혹은 자료구조에 부모 클래스로 데이터 타입을 지정하면 여러 자식 클래스를 저장 가능
  • instanceof 연산자로 참조변수가 참조하는 인스턴스의 실제 타입을 확인하는데 사용

Animal

package Chapter7;

public abstract class Animal {
    abstract void eatSomething();
}

Main

package Chapter7;

public class PolymorphismExample {

    public class Cat extends Animal {

        @Override
        void eatSomething() {
            System.out.println("고양이 사료 먹는중...");
        }
    }

    public class Dog extends Animal {

        @Override
        void eatSomething() {
            System.out.println("강아지 사료 먹는중...");
        }
    }

    public class Pig extends Animal {

        @Override
        void eatSomething() {
            System.out.println("돼지 사료 먹는 중..");
        }
    }

    public void main(String[] args) {
        Animal dog = new Dog();
        Cat cat = new Cat();
        Pig pig = new Pig();

        //instanceof 키워드
        System.out.println(cat instanceof Animal); // true

        // 부모 클래스 매개변수
        doEat(dog);
        doEat(cat);
        pig.eatSomething();
        
        // 부모 클래스 배열
        Animal animals[] = new Animal[3];
        
        animals[0] = new Dog();
        animals[1] = new Cat();
        animals[2] = new Pig();
        
    }

    private static void doEat(Animal animal){
        animal.eatSomething();;
    }
}

📍 Package란?

물리적으로 관련이 있는 클래스 파일들을 포함하는 하나의 디렉토리

  • 계층구조를 .(점)으로 구분
  • 모든 클래스는 반드시 한 패키지에 속해야 함
  • 같은 클래스 이름이라도 서로 다른 패키지에 속하면 구별이 가능

📍 Import문

Java 컴파일러에게 소스 파일에 사용된 클래스의 패키지에대한 정보를 제공하는 것

  • Import문으로 사용하고자 하는 class 파일의 패키지를 명시해주면 클래스 사용 시 생략 가능
  • Import문에 package.* 을 하더라도 하위 패키지의 모든 클래스를 포함하는 것은 아님
  • 주로 하위 패키지.* 로 구체적으로 명시
  • import static 문으로 작성하면 클래스 사용 시, 클래스 생략 가능
package Chapter7;

import java.*;
import java.io.*;
import java.util.ArrayList;
import static java.lang.System.out;

public class PackageAndImportExample {
    public static void main(String[] args) {
        out.println("Hello world");
        //System.out.println으로 사용안해도 된다.
    }
}

📍 제어자

변수, 메서드, 클래스 등에 대한 접근이나 어떤 기능적인 부분에 대해 지정해주는 것

  • 하나의 대상(변수,메서드,클래스)에 대해 여러 제어자를 조합하여 사용할 수 있으며,
    접근 제어자는 한가지만 가능
  • 각 대상의 선언부에 함께 표시

📍 제어자의 종류

접근 제어자

  • public, protected, default, private

그 외의 제어자

  • static, final, abstract, native, transient, synchronized, volatile, strictfp

📍 제어자 특징

static - 공통적인, 인스턴스가 아닌 클래스에 관련이 있음을 알리는 제어자

  • 멤버변수, 메서드, 초기화 블록에 사용 가능
    인스턴스를 생성하지 않고 호출이 가능

final - 마지막의, 변경할 수 없도록 하는 제어자

  • 클래스, 메서드, 멤버변수, 지역변수에 사용 가능
  • 생서자를 통해 final이 붙은 멤버변수를 초기화 할 수 있음

abstract - 추상의, 대상이 미완성임을 알리는 제어자

  • 클래스와 메서드에 사용 가능
  • 클래스에 사용하면 추상클래스(추상메서드가 포함된 클래스)
  • 메서드에 사용하면 추상메서드(구현은 하지 않고 선언만 된 메서드)

📍 접근 제어자 특징

외부에서 클래스 내부의 변수에 대한 접근을 제어함

  • 캡슐화와 밀접한 연관성

📍 제어자 규칙

  • 클래스에 abstract와 final을 동시에 사용할 수 없다.
  • 메서드에 static과 abstract를 같이 사용할 수 없다.
  • abstract 메서드의 접근제어자가 private면 안된다. -> 자식클래스에서 접근 구현 불가능
  • 메서드에 private와 final을 같이 사용할 필요는 없다.

📍 내부 클래스

긴밀한 관계가 있는 클래스를 클래스 안에 선언하여 사용하는 방법

  • 내부 클래스에서 외부클래스의 멤버들을 쉽게 접근 가능

  • 코드의 복잡성을 줄임

  • 내부 클래스의 종류는 변수 선언 위치와 동일(인스턴스,static,메서드 내부)

  • 내부클래스 중에서 satic 클래스만 static 멤버를 가질 수 있음

  • final과 static이 동시에 붙은 변수는 상수, 모든 내부 클래스에 정의가능

  • 인스턴스 클래스

  • 외부 클래스의 인스턴스 멤버를 객체 생성 없이 접근 가능

  • static 클래스는 바로 접근 가능

  • static 클래스

  • 외부 클래스의 인스턴스 멤버를 객체 생성 후 접근 가능

  • 인스턴스 클래스의 멤버 변수는 객체 생성 후 접근 가능

Plants

package Chapter7;

public class Plants {

    Sunflower sunflower = new Sunflower();

	//static class
    static class Rose {
        final static String name = "Rose";

        static void bloom(){
            System.out.println("장미가 피었습니다.");
        }
    }

	// instance class
    class Sunflower{
        final static String name = "Sunflower";

        void bloom(){
            System.out.println("해바라기가 피었습니다");
        }
    }


	//local class
    void makePlants(){

        class NewFlower{
            final static String name = "newFlower";

            void bloom(){
                System.out.println("새로운 꽃이 피었습니다");
            }
        }

        NewFlower newFlower = new NewFlower();
        newFlower.bloom();
    }
}

main

	package Chapter7;

public class InnerClassExample {
    public static void main(String[] args) {
        Plants.Rose.bloom();
        Plants plants = new Plants();
        plants.sunflower.bloom();
        plants.makePlants();
    }
}

📍 익명 클래스

이름이 없는 클래스로 추상 클래스 혹은 인터페이스를 바로 객체로 생성해서 반환

  • 단 하나의 추상클래스 혹은 인터페이스를 바로 생성, 일회용 클래스
  • 생성자가 없음
  • 클래스 마지막에 ;(세미콜론)을 붙여 구현이 끝났음을 표현
  • 인터페이스 혹은 추상 클래스의 미구현 메서드를 구현해야 함

Automobile

package Chapter7;

abstract class Automobile {
    abstract void move();
}

class Bus extends Automobile{

    @Override
    void move() {
        System.out.println("버스는 정류장을 따라 달린다.");
    }
}

class Taxi extends Automobile{

    @Override
    void move() {
        System.out.println("손님을 태우고 목적지까지 달린다");
    }
}

익명 클래스 사용

package Chapter7;

public class AnonymousClassExample {
    public static void main(String[] args) {
        Automobile bus = new Bus();
        Automobile taxi = new Taxi();
        Automobile automobile = new Automobile() {
            @Override
            void move() {
                System.out.println("자동차는 달린다");
            }
        };

        bus.move();
        taxi.move();
        automobile.move();
    }
}

📍 추상 클래스(abstract class)란?

Class에 abstract 제어자가 붙은 미완성인 클래스

  • 추상 메서드를 가진 클래스
  • 클래스를 인스턴스화 할 수 없음
  • 다중 상속 불가능

📍 추상 메서드(abstract method)

선언부만 존재하고, 구현부가 없는 메서드

  • 추상클래스를 상속받은 클래스에서는 추상 메서드를 반드시 구현해야 함
  • 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 함

하나의 추상 메서드를 가진 추상 클래스

package Chapter7;

public abstract class Animal {
	abstract void eatSomething();
}

추상 클래스 상속후 추상 메서드 구현하기

package chapter7;
public class Dog extends Animal{
	@Override
    void eatSomething(){
    	System.out.println("강아지는 사료를 먹습니다");
    }
}

📍 인터페이스(interface)란?

추상 메서드, 디폴트 메서드, static 메서드, 상수만 가진 클래스

  • 추상 클래스보다 추상화가 높음
  • 다중 상속 가능
  • 인스턴스 생성 불가
  • 다형성이 적용됨
  • 표준화 시켜 개발함으로써 개발자 간의 독립적인 개발이 가능(메서드만 공유하면 됨)

📍 인터페이스의 상속과 구현

인터페이스는 static상수만 정의 가능

조상 클래스의 멤버변수와 충돌 가능성이 거의 없음

개발자 간에 기능을 제공하는 쪽과 사용하는 쪽이 존재

  • 기능을 사용하는 쪽에서는 인터페이스 선언부만 알면 됨

interface

package Chapter7;

public interface Vehicle {
    void take();
    void move();
}

interface 구현

package Chapter7;

public class Car implements Vehicle{

    @Override
    public void take() {
        System.out.println("자동차 타기");
    }

    @Override
    public void move() {
        System.out.println("자동차 옮기기");
    }
}
	package Chapter7;

public class Airplane implements Vehicle{

    @Override
    public void take() {
        System.out.println("비행기 타기");
    }

    @Override
    public void move() {
        System.out.println("비행기 이동하기");
    }
}

📍 디폴트 메서드

인터페이스에 구현 가능한 메서드

  • 접근제어자는 public(생략가능), 반환 타입 앞에 default 표기, 추상 메서드와 다르게 구현해야 함
  • 디폴트 메서드와 기존 메서드의 이름이 중복되면 충돌이 발생
  • 여러 인터페이스의 디폴트 메서드 충돌 : 인터페이스를 구현한 클래스에서 오버라이딩
  • 상위 클래스와 디폴트 메서드 충돌 : 상위 클래스의 메서드가 상속됨

default 메서드 추가

package Chapter7;

public interface Vehicle {
    void take();
    void move();

    default void refuel(){
        System.out.println("Need to refuel gasoline");
    }
}

default 메서드 사용하기

package Chapter7;

public class DefaultMethodExample {
    public static void main(String[] args) {
        Vehicle car = new Car();
        Airplane airplane = new Airplane();

        car.move();
        car.take();
        car.refuel();

        airplane.move();
        airplane.take();
        airplane.refuel();
    }
}

📍 추상 클래스 vs 인터페이스

공통점

  • 인스턴스화가 불가능
  • 내부의 추상 메서드를 모두 구현해야 함

차이점

  • 인터페이스는 다중 상속이 가능, 추상 클래스는 단일 상속만 가능
  • 인터페이스는 제어자로 public static final 혹은 public abstract만 가능
  • 공통 기능과 사용 의도의 차이

📍 과제1(상속)

Human class

package Chapter7;

public abstract class Human {

    String name;
    String id;
    String gender;

    String job;

    Human(String name, String gender, String job){
        this.name = name;
        this.id = job + (int)(Math.random()*10000+1);
        this.gender = gender;
        this.job = job;
    }

    abstract public void showRole();

    void updatgInfo(String changeName, String gender){
        this.name = changeName;
        this.gender = gender;
        this.id = this.job + (int)(Math.random()*10000+1);
    }
}

Police class

package Chapter7;

public class Police extends Human {

    Police(String name, String gender, String job) {
        super(name, gender, job);
    }

    @Override
    public void showRole() {
        System.out.println("경찰입니다");
        System.out.println("ID : " + this.id );
        System.out.println("이름 : " + this.name);
        System.out.println("성별 : " + this.gender);
    }
}

Doctor class

package Chapter7;

public class Doctor extends Human{


    Doctor(String name, String gender, String job) {
        super(name, gender, job);
    }

    @Override
    public void showRole() {
        System.out.println("의사 입니다");
        System.out.println("ID : " + this.id );
        System.out.println("이름 : " + this.name);
        System.out.println("성별 : " + this.gender);
    }
}

Developer class

package Chapter7;

public class Developer  extends Human{


    Developer(String name, String gender, String job) {
        super(name, gender, job);
    }

    @Override
    public void showRole() {
        System.out.println("개발자 입니다");
        System.out.println("ID : " + this.id );
        System.out.println("이름 : " + this.name);
        System.out.println("성별 : " + this.gender);
    }
}

class 사용하기

package Chapter7;

public class task {
    public static void main(String[] args) {
        Human police = new Police("둘리","남","경찰");
        Human doctor = new Doctor("또치","남","의사");
        Developer developer = new Developer("마이콜","여","개발");

        police.showRole();
        doctor.showRole();
        developer.showRole();

        police.updatgInfo("박상윤","여");
        System.out.println("name"+police.name+" gender"+police.gender);
    }
}

출력결과

📍 과제2(인터페이스)

Controller

package Chapter7;

public interface Controller {

    void pressPlusButton();

    void pressMinusButton();

    void pressUPButton();

    void pressDownButton();

    void on();

    void off();
}

TvController

package Chapter7;

public class TvController implements Controller{

    boolean powerFlag;

    int channel = 0;

    int volumn = 0;

    TvController(int channel, int volumn){
        this.channel = channel;
        this.volumn = volumn;
        this.powerFlag = true;
    }

    @Override
    public void pressPlusButton() {

        if(powerFlag){
            System.out.println("채널 올려");
            System.out.println("현재 채널 : " + ++this.channel);
        }else{
            System.out.println("티비 꺼져 있음");
        }


    }

    @Override
    public void pressMinusButton() {
        if(powerFlag){
            System.out.println("채널 줄여");
            System.out.println("현재 채널 : " + --this.channel);
        }else{
            System.out.println("티비 꺼져 있음");
        }
    }

    @Override
    public void pressUPButton() {

        if(powerFlag){
            System.out.println("볼륨 올려");
            System.out.println("현재 볼륨 : " + ++this.volumn);
        }else{
            System.out.println("티비 꺼져 있음");
        }


    }

    @Override
    public void pressDownButton() {

        if(powerFlag){
            System.out.println("볼륨 줄여");
            System.out.println("현재 볼륨 : " + --this.volumn);
        }else{
            System.out.println("티비 꺼져 있음");
        }


    }

    @Override
    public void on() {

        if(this.powerFlag){
            System.out.println("이미 켜짐");
            return;
        }

        System.out.println("티비 켜짐");
        powerFlag = true;
    }

    @Override
    public void off() {

        if(!this.powerFlag){
            System.out.println("이미 꺼짐");
            return;
        }

        System.out.println("티비 꺼짐");
        powerFlag = false;
    }

    public void showTvInformation(){
        if(powerFlag){
            System.out.println("전원 상태:" + this.powerFlag);
            System.out.println("channel:"+this.channel);
            System.out.println("volumn:"+this.volumn);
        }else{
            System.out.println("전원꺼져있다.");
        }

    }
}

TV controller 사용하기

package Chapter7;

public class task2 {
    public static void main(String[] args) {
        TvController remote = new TvController(24,19);

        remote.on();
        remote.pressDownButton();
        remote.pressMinusButton();
        remote.pressPlusButton();
        remote.pressUPButton();
        remote.showTvInformation();
        remote.off();
    }
}

출력결과

profile
BE 공부 블로그입니다

0개의 댓글