늦잠을 자버렸다.
물론 9시전에 줌을 키긴했지만….
원래는 8시20분에 일어나 천천히 준비하고 줌을 키는데
오늘은 8시50분에 일어나버려서 바로 모자만쓰고 줌을 켰다.

image

너무 졸리다….
어제의 게임이 이 졸림의 근원.
공부에 집중도를 높이기위해선 게임을 안하는게
최고의 선택인 것 같다… 알면서도 끊지못하는
이 다이어트같은놈. 매우 피곤하지만 정신을 부여잡고

공부를 시작해보자 👀


저번 시간에 공부했던 내용은 상속과 캡슐화이다.
이번에는 객체지향 프로그래밍의 4개의 기둥인 나머지 2개
다형성과 추상화를 배워볼 시간이다.

오늘은 다형성에 대해서만 정리해보고 내일 추상화를 정리하려고 한다.
내가 이해한 것이 맞을 지는 모르겠지만 기록해보면서 정리해보자

다형성

다형성이란?

하나의 객체가 여러 가지 형태를 가질 수 있는 성질을 의미한다.
자바 프로그래밍의 다형성은

한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것‘을 의미한다.
다시한번 정의하자면 ‘상위클래스 타입의 참조변수로 하위클래스의 객체를 참조‘할 수 있다.

public class FriendTest {
	public static void main(String[] args) {
        Friend friend = new Friend(); // 객체 타입과 참조변수 타입의 일치 -> 가능
        BoyFriend boyfriend = new BoyFriend(); // 객체 타입과 참조변수 타입의 일치 -> 가능
        Friend girlfriend = new GirlFriend(); // 객체 타입과 참조변수 타입의 불일치 -> 가능
	//GirlFriend friend1 = new Friend(); -> 하위클래스 타입으로 상위클래스 객체 참조 -> 불가능
    }
}
    
class Friend {}
class BoyFriend extends Friend {}
class GirlFriend extends Friend {}

위의 코드를 예제로 보았을때

Friend라는 상위클래스와
BoyFriend, GirlFriend라는 하위클래스를 상속하고 있는데
여기서 중요한점은 객체 타입과 참조변수의 타입이 일치하지 않는데
객체 타입이 상위클래스인 경우 하위클래스를 객체로 참조변수에 담을 수 있다.
좀더 자세히 얘기해자 다른타입의 객체와 참조변수가 달랐을때의

멤버변수를 세어보자

class Person{ 
    String name;
    int age;
    
    void eat(){
        System.out.println("밥을 먹는다.");
    }
    
    void sleep(){
        System.out.println("잠은 잔다.");
    }
}
// Person 클래스의 멤버는 4개 (변수 2개 + 메서드2개)


class Programmer extends Person{
    void program(){
   	 System.out.println("프로그램을 한다.");
    }
}

// Programmer의 멤버는 5개 (상위 클래스 멤버4개 + 자신클래스의 메서드 1개)

위의 경우 Person 클래스는 4개의 멤버가 있고
Programmer 클래스는 Person을 상속받기에 5개의 멤버가 있다.
여기서 프로그램으로 표현 해보자면 이렇게 멤버를 사용이 가능하다.

Person person = new Person(); // -> 4개의 멤버 사용가능
Programmer programmer = new Programmer(); // -> 6개의 멤버 사용가능

하지만 상위클래스인 Person 타입으로 Programmer라는 클래스의 객체로
programmer라는 참조면수에 할당하게 되면 멤버가 몇개 일까?

Person programmer = new Programmer();

정답은 4개이다.

이유는 Person의 멤버가 4개이기 때문이고 멤버는 Person 클래스의
정의되어있는 변수와 메서드만 사용이 가능하다.

그말은 즉슨

programmer.name programmer.age programmer.eat(); programmer.sleep();

이렇게 4가지 멤버만 사용이 가능하다는 뜻이다. programmer.program(); 메서드는
멤버에 해당하지 않기때문에 실행 불가능.


참조변수의 타입 변환

참조변수의 타입변환은 사용할 수 있는 멤버의 개수를 조절한다는 것 같다.
위에서 예제로 알아봤듯이 참조변수의 타입이 Person이냐 Programmer이냐 에따라
멤버의 개수가 4개 5개 이렇게 바뀐 모습을 볼 수 있기 때문이다.

타입 변환을 위해서 아래와 같은 조건이 충족해야한다.
1. 서로 상속관계에 있는 상위 클래스 - 하위 클래스 사이에만 타입 변환이 가능합니다.
2. 하위 클래스 타입에서 상위 클래스 타입으로의 타입 변환(업캐스팅)은 형변환 연산자(괄호)를 생략할 수 있습니다.
3. 반대로 상위 클래스에서 하위 클래스 타입으로 변환(다운캐스팅)은 형변환 연산자(괄호)를 반드시 명시해야합니다.

타입 캐스팅(변환)의 코드 예제이다.

Car car = new Car();
Vehicle vehicle = (Vehicle) car; // 상위 클래스 Vehicle 타입으로 변환(생략 가능)
Car car2 = (Car) vehicle; // 하위 클래스 Car타입으로 변환(생략 불가능)
MotorBike motorBike = (MotorBike) car; // 상속관계가 아니므로 타입 변환 불가 -> 에러발생

Vehicle이라는 상위클래스가 있고
상속받은 Car, MotorBike라는 하위클래스가 있다고 가정해보자. 하위클래스로 만든 객체인 car라는 참조변수를
Vehicle이라는 타입으로 업캐스팅할 수 있다.
(*업캐스팅: 보다 위로 형변환을함)

그 형 변환을 하여 저장한 참조변수 vehicle을 다시
하위 클래스 Car타입으로 다운캐스팅을 할 수 있다.
여기서 중요한점은 하위클래스인 Car와, MotorBike끼리는 타입캐스팅이 불가능하다.

또 하나로 타입캐스팅이 안되는 부분이 있는데

Vehicle vehicle = new Vehicle();
Car car = (Car)vehicle(); // 에러

전제조건은 _업캐스팅이 선행되어야 다운 캐스팅_이 가능하다.
이것이 포인트이다.

Vehicle vehicle = new Car(); // 업캐스팅이 선행됨
Car car = (Car)vehicle(); // 가능

위와 같이 하위객채(Car();)로 인스턴스를 만들었다.
위에 구문은 Vehicle vehicle = (Vehicle) new Car();
같은 구문이다. 즉 생성될때부터 업캐스팅이 되어지는 것이고
업캐스팅될때 이것이 생략되어있기때문에 그런 것이다.
위의 같은 경우는 다운캐스팅이 가능하다. 이유는 업캐스팅이 선행되었기때문에.

정리해보자면 애초에 생성된 객체가 하위클래스로
다운 캐스팅하는 것은 불가능한 것 같다.
(Vehicle 객체에서 하위클래스인 Car로 캐스팅이 안되는 것과 같이)


instanceof

여기서 타입캐스팅이 가능한지 여부에 대한
확인하는 명령어가 있다.

Vehicle vehicle = new Vehicle();
System.out.println(vehicle instanceof Object); //true
System.out.println(vehicle instanceof Vehicle); //true
System.out.println(vehicle instanceof Car); //false

위와 같이 만들어진 객체 기준으로 타입 캐스팅이 가능한지
여부를 boolean값으로 return해주는 기능이다.

첫번째 Object는 상위클래스이므로 당연히 업캐스팅이 가능하다 // true
두번째 Vehicle은 자기자신 클래스이므로 당연히 가능하다 // true
세번째 Car는 하위클래스이다. 하위클래스는 상위클래스를 참조하지 못한다 // false

비유를 쉽게하자면 상위클래스 = 부모 / 하위클래스 = 자식 인데,
자식은 내집과 부모집을 마음대로 왔다갔다 할 수 있지만
부모는 자식집을 마음대로 왔다갔다 할 수 없다.
부모기준에서 생성된 객체는 하위클래스인 자식집을 가지 못하는 것

즉 참조를 못한는 것 같다.


다형성의 활용

한방에 정리해보자.

다형성이란 상위클래스 타입의 참조변수로 하위클래스의 객체를 참조 이말만 기억하면 된다.
예를들어 커피집에 대한 다형성 활용을 보자면
커피집에는 손님이 있고, 커피 가격과 종류, 고객이 있다.

Custmoer custmoer = new Custmoer();
custmoer.buyCoffee(new Americano());

Custmoer의 buyCofferr 매소드를 호출
new Americano(); 객체를 생성자로 호출

public class Custmoer {
    int money = 50000;
    void buyCoffee(Coffee coffee){
        if(money<coffee.price){
            System.out.println("잔액이 부족합니다.");
            return;
        }
        money -= coffee.price;
        System.out.println(coffee+"를 구입했습니다");
    }
}

buyCoffee를 호출하였을때
Coffer coffee = new Americano(); 이렇게 매개변수로 들어가게 된다.
그렇게 되면 Americano(); 생성자가 호출되게 되고

class Americano extends Coffee {

    public Americano() {
        super(4000);
    }
    public String toString(){
        return "카페라떼";
    }
}

아메리카노 생성자 public Americano()의 내용 에는
super(4000); 상위 클래스의 생성자를 호출하고
인자로 4000의 값을 넣어 두었다.

class Coffee {
    int price;

    public Coffee(int price) {
        this.price = price;
    }
}

그렇게 될 경우 상위 클래스인 Coffee의
생성자에 int price의 매개변수에 4000이란 값이 매칭되고
인스턴스 변수인 this.price에 4000원이란 값이 책정되게 되어지는 것이다.
이제 생성자로 초기화가 완료된 객체인 coffee로 coffee.price를 이용해
현재 가지고있는 인스턴스 변수인 money = 50000;과 함께
가격을 차감하며 계산이 가능한 것이다.

여기서 중요한건 coffee참조변수의 멤버는
상위 클래스인 Coffee의 인스턴스 변수인 price 한개 밖에 없다.
(생성자는 멤버에 포함되지 않음)


마치며…

내가 작성하면서도 개념이 이해가 쉽지가않다.
아직 정확하지 않는 정보들일 수도 있고
조금더 글을 적으면서 내가 적은게 맞는지
단어선택도 신중하게쓰고 연습을 많이 해야겠다!!

오늘 공부는 여기서 끝!


오늘의 커피량: ☕️ ☕️ ☕️ ☕️️️️
오늘의 점심: 라면, 계란부침, 공기밥