JAVA - 09

월요일좋아·2022년 10월 7일
0

JAVA

목록 보기
9/12

추상 클래스

추상 클래스 개념

  • 추상
    • 실체들 간에 공통되는 특성을 추출한 것
      • ex1) 새, 곤충, 물고기 -> 동물(추상)
      • ex2) 삼성, 현대, LG -> 회사(추상)
  • 추상 클래스
    • 몸통이 없어서 객체 생성 불가능 실체클래스의 부모클래스로서의 역할만 함
    • 실체 클래스들의 공통되는 필드와 메소드 정의한 클래스
    • 추상 클래스는 실체 클래스의 부모 클래스 역할 (단독 객체 X)

추상 클래스의 용도

  • 실체 클래스의 공통된 필드와 메소드의 이름을 통일할 목적
    • 실체 클래스를 설계자가 여러 사람일 경우
    • 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있음.
  • 실체 클래스를 작성할 때 시간절약
    • 실체 클래스는 추가적인 필드와 메소드만 선언
  • 실체 클래스 설계 규격을 만들고자 할 때
    • 실체 클래스가 가져야 할 필드와 메소드를 추상 클래스에 미리 정의
    • 실체 클래스는 추상 클래스를 무조건 상속 받아 작성

추상 클래스 선언

  • 클래스 선언에 abstract 키워드 사용
    • New 연산자로 객체 생성하지 못하고, 상속을 통해 자식 클래스만 객체 생성 가능

chap05_1 - Phone.class, SmartPhone.class, PhoneEx.class

Phone.class

public abstract class Phone {   // abstract 추가 : 추상 클래스
    public String owner;

    public Phone(String owner) {
        this.owner = owner;
    }

    public void turnOn() {
        System.out.println("휴대폰 전원을 켭니다.");
    }

    public void turnOff() {
        System.out.println("휴대폰 전원을 끕니다.");
    }
}

SmartPhone.class

public class SmartPhone extends Phone{  // Phone 클래스를 상속받아 사용
    public SmartPhone(String owner) {
        super(owner);   // 부모클래스 Phone에 owner 대입 -> 본인의 멤버변수 owner로 저장
    }

    public void internetSearch() {
        System.out.println("인터넷 검색을 합니다.");
    }
}

PhoneEx.class

public class PhoneEx {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone("홍길동");
        smartPhone.turnOn();
        smartPhone.internetSearch();
        smartPhone.turnOff();

        // Phone 클래스는 추상 클래스 이므로 new 키워드를 통해 (직접적인)객체를 생성할 수 없음
        // Phone phone = new Phone("홍길동"); // 에러 발생
        Phone phone; // 해당 클래스 타입의 변수 만드는건 가능 , new 를 못씀
    }
}

추상 메소드와 오버라이딩(재정의)

  • 메소드 이름 동일하지만, 실행 내용이 실체 클래스마다 다른 메소드
    ex) 동물은 소리를 낸다. 하지만 실체 동물들의 소리는 제각기 다르다.
  • 구현 방법
    • 추상 클래스에는 메소드의 선언부만 작성 (추상 메소드)
    • 실체 클래스에서 메소드의 실행 내용 작성(오버라이딩(Overriding))
  • 추상 클래스
    • 추상 메서드를 1개 이상 가지고 있는 클래스를 추상 클래스라고 함
    • 자식 클래스가 추상 클래스를 상속받았을 경우, 반드시 추상 메서드를 오버라이딩 해야 함
      (만약 오버라이딩 하지 않을 경우 자식 클래스도 추상클래스로 선언해야 함)
    • 추상 클래스는 객체를 생성할 수 없음
    • 추상 클래스는 자식 클래스를 규격화하기 위해 사용함

chap05-1 - Aminal.class, Dog.class, Cat.class, AnimalEx.class
Aminal.class

public abstract class Animal {

    public String kind;

    public Animal(String kind) {
        this.kind = kind;
    }

    public void breathe() {
        System.out.println(this.kind + "가 숨을 쉽니다.");
    }

    public abstract void sound();
}

Dog.class

public class Dog extends Animal{

    public Dog(String kind) {
        super(kind);
    }

    @Override
    public void sound() {
        System.out.println(kind + "는 멍멍");
    }
}

Cat.class

public class Cat extends Animal{
    public Cat(String kind) {
        super(kind);
    }

    @Override
    public void sound() {
        System.out.println(kind + "는 냐옹");
    }
}

AnimalEx.class

public class AnimalEx {

    public static void animalSound(Animal animal) {
        animal.sound();
    }

    public static void main(String[] args) {
        Dog dog = new Dog("멍멍이");
        Cat cat = new Cat("고양이");

        dog.breathe();
        dog.sound();
        cat.breathe();
        cat.sound();
        System.out.println("--------------------");



        // animal = new Animal(); <-- 오류발생: 추상클래스는 상속받아서만 사용 가능, 객체 생성 불가능

        // 추상 클래스 타입의 변수를 선언하는 것은 문제가 없음.
        Animal animal;

        // 클래스의 형변환(다형성)
        animal = new Dog("멍멍이");
        animal.sound(); // 결과 : 멍멍

        animal = new Cat("고양이");
        animal.sound(); // 결과 : 냐옹

        animalSound(new Dog("강아지"));    // 결과 : 강아지는 멍멍
        animalSound(new Cat("냐옹이"));    // 결과 : 냐옹이는 냐옹
    }
}

타입 변환과 다형성

다형성

  • 같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질
    • 부모 타입에는 모든 자식 객체가 대입 가능
      - 자식 타입은 부모 타입으로 자동 타입 변환

자동 타입 변환(형 변환)

  • 프로그램 실행 도중에 자동 타입 변환이 일어나는 것
Animal animal = new Dog();

  • 바로 위의 부모가 아니더라도 상속 계층의 상위면 자동 타입 변환 가능
    • 변환 후에는 부모 클래스 멤버만 접근 가능

필드의 다형성

  • 다형성을 구현하는 기술적 방법
    • 부모 타입으로 자동 변환
    • 재정의된 메소드(오버라이딩)

다형성 예제 1

study05_1 - Parent.class, Child.class, ChildEx.class
Parent.class

public class Parent {
    public void method1() {
        System.out.println("Parent-method1");
    }

    public void method2() {
        System.out.println("Parent-method2");
    }
}

Child.class

public class Child extends Parent{
    @Override
    public void method2() {
        System.out.println("Child-method2()");
    }

    public void method3() {
        System.out.println("Child-method3()");
    }
}

ChildEx.class

public class ChildEx {
    public static void main(String[] args) {

        Parent parent = new Parent();  // 부모 클래스 객체 생성
        parent.method1(); // 결과 : Parent-method1
        parent.method2(); // 결과 : Parent-method2

        System.out.println("--------------");
        
        // Parent 상속받은 Child
        // 객체 생성. Child()에는 method1이 없음, method2는 오버라이드 되어있음
        Child child = new Child();

        child.method1(); // 결과 : Parent-method1 (상속받은 메소드)
        child.method2(); // 결과 : Child-method2()
        child.method3(); // 결과 : Child-method3()

        System.out.println("--------------");

        // 다형성 : 부모 클래스 타입의 변수에 자식 클래스 타입의 객체를 대입하여 부모 클래스 타입의 객체인 것처럼 사용하는것,
        //         실제 결과물은 자식 클래스 타입의 객체가 가지고 있는 데이터로 출력이 되는것.

        parent = child; // parent 클래스 타입(부모)에 child 객체(자식) 대입 - 문제없음

        parent.method1(); // 결과 : Parent-method1

        parent.method2(); // 결과 : Child-method2() <-- WHY? 껍데기는 부모처럼 생겼지만 알맹이는 Child객체이기때문.

        // parent.method3(); : 실행이 안됨 <-- WHY? 알맹이는 Child인데 왜 안되는가?
        //                     일단은 부모클래스 타입으로 로드가 되었기때문에 자식클래스에서 전용으로 있던것은 숨겨짐.
        //                     원래 부모님이 하셨던 행동만 사용 가능
        //                     (= 부모 클래스에 없던 메소드<자식클래스에만 있던 메소드>는 사용 불가능)
        //                     부모 클래스 타입의 변수에 자식 클래스 타입의 갹체를 대입했을 경우
        //                     자동 형변환이 발생하여 자식 클래스 타입의 객체가 전용으로 가지고 있던 멤버를 활용할 수 없음.
        //                     부모 클래스 멤버만 사용할 수 있음.
    }
}

다형성 예제 2

study05_1 - Tire.class, Car2.class, HankookTire.class, KumhoTire.class, Car2Ex.class, Car3.class
Tire.class

public class Tire {
    public int maxRotation; // 최대 회전수
    public int accumulatedRotation; // 누적 회전수
    public String location; // 타이어 위치
    
    public Tire(String location, int maxRotation) {
        this.location = location;
        this.maxRotation = maxRotation;
    }
    
    public boolean roll() {
        ++ accumulatedRotation;

        if (accumulatedRotation < maxRotation) {
            System.out.println(location + "Tire 수명 : " + (maxRotation - accumulatedRotation) + "회");
            return true;
        }
        else {
            System.out.println("*** " + location + " Tire 펑크 ***");
            return false;
        }
    }
}

Car2.class

public class Car2 {
    Tire frontLeftTire = new Tire("앞왼쪽", 6);
    Tire frontRightTire = new Tire("앞오른쪽", 2);
    Tire backLeftTire = new Tire("뒤왼쪽", 3);
    Tire backRightTire = new Tire("뒤오른쪽", 4);

    int run() {
        System.out.println("[자동차가 달립니다.]");
        if (frontLeftTire.roll() == false) {
            stop();
            return 1;
        }
        if (frontRightTire.roll() == false) {
            stop();
            return 2;
        }
        if (backLeftTire.roll() == false) {
            stop();
            return 3;
        }
        if (backRightTire.roll() == false) {
            stop();
            return 4;
        }
        return 0;
    }

    void stop() {
        System.out.println("[자동차가 멈춥니다.]");
    }
}

HankookTire.class

public class HankookTire extends Tire{

    public HankookTire(String location, int maxRotation) {
        super(location, maxRotation);
    }

    @Override
    public boolean roll() {
        ++accumulatedRotation;

        if (accumulatedRotation < maxRotation) {
            System.out.println(location + "HankookTire 수명 : " + (maxRotation - accumulatedRotation) + "회");
            return true;
        }
        else {
            System.out.println("*** " + location + " HankookTire 펑크 ***");
            return false;
        }
    }
}

KumhoTire.class

public class KumhoTire extends Tire {

    public KumhoTire(String location, int maxRotation) {
        super(location, maxRotation);
    }

    @Override
    public boolean roll() {
        ++accumulatedRotation;
        if (accumulatedRotation < maxRotation) {
            System.out.println(location + "KumhoTire 수명 : " + (maxRotation - accumulatedRotation) + "회");
            return true;
        } else {
            System.out.println("*** " + location + " KumhoTire 펑크 ***");
            return false;
        }
    }
}

Car2Ex.class

import package2.C;

public class Car2Ex {
    public static void main(String[] args) {
        Car2 car = new Car2();

        for(int i=1; i<=5; i++) {
            int problemLocation = car.run();

            switch (problemLocation) {
                case 1:
                    System.out.println("앞왼쪽 HankookTire로 교체");
                    // 다형성 활용 (부모 클래스 타입에 자식 클래스 대입)
                    car.frontLeftTire = new HankookTire("앞왼쪽", 15);
                    break;

                case 2:
                    System.out.println("앞오른쪽 KumhoTire로 교체");
                    // 다형성 활용 (부모 클래스 타입에 자식 클래스 대입)
                    car.frontRightTire = new KumhoTire("앞오른쪽", 13);
                    break;

                case 3:
                    System.out.println("뒤왼쪽 HankookTire로 교체");
                    // 다형성 활용 (부모 클래스 타입에 자식 클래스 대입)
                    car.backLeftTire = new HankookTire("뒤왼쪽", 14);
                    break;

                case 4:
                    System.out.println("뒤오른쪽 KumhoTire로 교체");
                    // 다형성 활용 (부모 클래스 타입에 자식 클래스 대입)
                    car.backRightTire = new KumhoTire("뒤오른쪽", 17);
                    break;
            }
            System.out.println("---------------------------------------");
        }
        
        // 다형성 이용한 Car3 이용
        System.out.println("\n\n");

        Car3 car3 = new Car3();

        for(int i = 1; i <= 5; i++) {
            int problemLocation = car3.run();

            if (problemLocation != 0) {
                System.out.println(car3.tires[problemLocation -1].location + "HankookTire로 교체");
                car3.tires[problemLocation -1] = new HankookTire((car3.tires[problemLocation -1].location), 15);
            }
            System.out.println("------------------------------------");
        }
    }
}

매개변수의 다형성

  • 매개변수가 클래스 타입일 경우
    • 해당 클래스의 객체 대입이 원칙이나 자식 객체 대입하는 것도 허용
      • 자동 타입 변환
      • 매개변수의 다형성

매개변수의 다형성 예제1

Vehicle, Bus, Driver, Taxi, DriverEx 클래스

Vehicle.class

public class Vehicle {
    public void run() {
        System.out.println("차량이 달립니다.");
    }
}

Bus.class

public class Bus extends Vehicle {
    @Override
    public void run() {
        System.out.println("버스가 달립니다.");
    }
}

Taxi.class

public class Taxi extends Vehicle {
    @Override
    public void run() {
        System.out.println("택시가 달립니다.");
    }
}

Driver.class

public class Driver {
    public void drive(Vehicle vehicle) {
        vehicle.run();
    }
}

DriverEx.class

public class DriverEx {
    public static void main(String[] args) {
        Driver driver = new Driver();

        Bus bus = new Bus();
        Taxi taxi = new Taxi();
        Vehicle vehicle = new Vehicle();

        //      각각의 객체로 실행하는 방식
        vehicle.run();
        bus.run();
        taxi.run();

//        다형성을 이용하여 부모 클래스 타입의 변수에 자식 클래스 타입의 객체를 대입
        vehicle = bus;  // 다형성
        vehicle.run();
        vehicle = taxi;
        vehicle.run();

//        다른 클래스의 멤버 메서드의 매개변수로 부모클래스 타입의 변수를 사용 시,
//		  자식 클래스 타입의 객체를 매개변수로 넘겨서 사용할 수 있음.
//        사용하는 방법은 동일한데, 사용된 데이터에 따라서 다른 출력을 하고 있음
        driver.drive(bus);
        driver.drive(taxi);

    }
}

강제 타입 변환

  • 부모 타입을 자식 타입으로 변환하는 것
  • 조건
    • 자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때
  • 강제 타입 변환 이 필요한 경우
    • 자식 타입이 부모 타입으로 자동 변환
      • 부모 타입에 선언된 필드와 메소드만 사용 가능
    • 자식 타입에 선언된 필드와 메소드를 다시 사용해야 할 경우

객체 타입 확인(instanceof)

  • 부모 타입이면 모두 자식 타입으로 강제 타입 변환할 수 있는 것 아님

    • ClassCastException 예외 발생 가능
  • 먼저 자식 타입인지 확인 후 강제 타입 실행해야 함

강제 타입 변환 예제

Parent2.class

public class Parent2 {
    public String field1;

    public void method1() {
        System.out.println("부모 메서드1 실행");
    }

    public void method2() {
        System.out.println("부모 메서드2 실행");
    }
}

Child2.class

public class Child2 extends Parent2 {
    public String field2;

    public void method3() {
        System.out.println("자식 메서드3 실행");
    }
}

Child2Ex.class

public class Child2Ex {
    public static void main(String[] args) {
//        자동 타입 변환
//        부모 클래스 타입의 변수에 자식 클래스 타입의 객체를 대입하면 부모 클래스 타입의 멤버만 사용이 가능함(알맹이는 자식 클래스 타입의 객체)
//        부모흉내를 내고있어서 본인 전용 메소드 사용 못함
        Parent2 parent2 = new Child2();
        parent2.field1 = "data1";
        parent2.method1();
        parent2.method2();
//에러    parent2.field2 = "데이터 2";
//에러    parent2.method3();

        System.out.println("\n----- 자식 클래스 타입의 변수에 자식 클래스 타입의 객체 대입 -----\n");
//        자식 클래스 타입의 변수에 자식 클래스 객체를 대입하면 부모클래스에서 상속받은것 + 자식 전용 필드 모두 사용 가능
        Child2 child2 = new Child2();
        child2.field1 = "데이터 1";
        child2.field2 = "데이터 2";
        child2.method1();
        child2.method2();
        child2.method3();

        System.out.println("\n----- 다시 자식 클래스 타입으로 돌아갈 때 -----\n");
        // 자식 클래스 타입의 객체 생성
        Child2 child21 = new Child2();
        child21.field1 = "Parent2에서 상속받은 멤버 변수";
        child21.field2 = "Child2 전용으로 가지고 있는 멤버 변수";
        System.out.println("객체 child21의 field1 출력: " + child21.field1);
        System.out.println("객체 child21의 field2 출력: " + child21.field2);
        System.out.println("객체 child21의 method1() 실행");
        child21.method1();
        System.out.println("객체 child21의 method2() 실행");
        child21.method2();
        System.out.println("객체 child21의 method3() 실행");
        child21.method3();

        System.out.println("\n부모 객체로 자동 타입 변환\n");

        Parent2 parent21 = child21; // child21을 parent21에 대입
        System.out.println("객체 parent21의 filed1 출력 : " + parent21.field1);
//에러        System.out.println("객체 parent21의 filed2 출력 : " + parent21.field2);
        System.out.println("객체 parent21의 method1() 실행");
        parent21.method1();
        System.out.println("객체 parent21의 method2() 실행");
        parent21.method2();
        System.out.println("객체 parent21의 method3() 실행");
//에러        parent21.method3();

        System.out.println("\nparent21을 다시 자식 클래스 타입으로 변환\n");
//        자식 클래스 타입의 변수에 부모 클래스 타입의 갹체를 대입하면 오류 발생
//        원본 객체를 자식클래스 타입의 변수로 타입 변환 시 해당 객체의 모든 멤버를 가지고 있는지 알 수 없기 때문에 강제 타입변환을 해야 함
//오류        Child2 child22 = parent21;
        Child2 child22 = (Child2)parent21; // 강제 형변환

        System.out.println("객체 child22의 field1 출력: " + child22.field1);
        System.out.println("객체 child22의 field2 출력: " + child22.field2);
        System.out.println("객체 child22의 method1() 실행");
        child22.method1();
        System.out.println("객체 child22의 method2() 실행");
        child22.method2();
        System.out.println("객체 child22의 method3() 실행");
        child22.method3();

        System.out.println("\n다시 자식 클래스 타입으로 변환되는 조건\n");
//        1. 원본이 자식 클래스 타입이어야 해당하는 자식 클래스 타입으로 변환이 가능
        
/*//        원본이 부모 클래스 타입
        Parent2 parent23 = new Parent2();
//        자식 클래스 타입의 변수 선언
        Child2 child23;
//        자식 클래스 타입의 변수에 부모 클래스 타입의 객체를 대입
        child23 = (Child2) parent23;    // 문법상으로는 문제없지만 실행하면 오류 발생! why? 원본이 부모 클래스 타입이라서.
                                        // child23 객체는 부모클래스 parent2의 자식 클래스이기때문에 문법상의 오류는 없지만
                                        // 원본이 자식 클래스 타입의 객체가 아니기 때문에 로직 에러(런타임 에러)가 발생함
//        자식 클래스 타입의 객체의 멤버 사용
        child23.method3();*/

        Parent2 parent24 = new Child2();
        castMethod1(parent24);
        castMethod2(parent24);

        Parent2 parent25 = new Parent2();
        castMethod1(parent25);  // Child2로 변환되지 않음
        castMethod2(parent25);  // 런타임에러 발생


    }

    public static void castMethod1(Parent2 parent) {
//        instanceof 연산자를 사용하면 현재의 객체가 지정한 타입의 객체인지 확인이 가능함.
        if (parent instanceof Child2) {
            Child2 child = (Child2) parent;
            System.out.println("castMethod1 - Child2로 변환 성공");
        }
        else {
            System.out.println("castMethod1 - Child2로 변환되지 않음");
        }
    }

    public static void castMethod2(Parent2 parent) {
//        원본 객체가 변환하려는 클래스 타입의 객체가 아닐 수 있기 때문에 오류가 발생할 수 있음.
        Child2 child = (Child2) parent;
        System.out.println("castMethod2 - Child2로 변환 성공");
    }
}

0개의 댓글