날아라쩡글이의 블로그입니다.

클래스의 형변환 본문

중앙 HTA (2106기) story/java story

클래스의 형변환

날아라쩡글이 2021. 9. 17. 06:54
반응형

클래스 형변환 활용하기

1. 부모타입의 참조변수로 모든 자식객체를 참조할 수 있다. (강제형변환사용시!)

  • 상속관계에 있는 (상위 객체 -하위객체 )관계에 있는 객체사이에서만 클래스 형변환이 가능하다. 
  • 하위 객체를 상위 클래스 타입의 참조변수로 참조하는 것이 클래스 형변환이다. 
  • 클래스 형변환 없이 객체를 참조하는 경우 상위객체의 공개된 모든 속성 및 기능을 사용할 수 있고, 자기 자신의 공개된 모든 속성 및 기능을 사용할 수 있다. 
  • Object 타입의 참조변수는 자바의 모든객체 (배열을 포함한 )를 참조할 수 있다. 
  • 상위클래스 타입의 객체를 하위클래스 타입의 참조변수로 참조하는 것은 절대 불가능하다. 
  • 생성된 객체와 참조변수의 타입이 상속관계가 아닌 경우도 클래스 형변환이 절대 불가능하다. 클래스 형변환은 
  • 생성한 객체의 타입과 참조하는 변수의 타입이 서로 다르다. 
  • 생성한 객체의 타입은 하위클래스 타입이고, 참조하는 변수의 타입은 상위 클래스 타입이다.
  • 그렇게 생성한 참조변수는 상위클래스 타입밖에 사용할 수 없다. 
  • 하위클래스를 사용할 수 있는 방법은 존재하는데, 바로 강제 형변환을 사용해주는 것이다. 
  • 상위클래스로 가는 것을 내부의 super(), super(상위객체의 주소값)을 찾았다면 강제 형변환의 경우는 외부로 가는 것이다. 그렇게 찾고 (하위클래스)참조변수 dot 하위클래스의 기능을 작성해준다면 사용할 수 있다. 
  • 단, 강제형변환을 한다고해서 참조변수의 참조값은 변하는 것은 아니다--->상위클래스를 향하고 있다. 
  • 내가 생성한 부모객체지만, 자식은 부모를 참조하는 것은 안된다. 
  • ex) Palisade c4 = new Car XXXX --->하위클래스객체에 대한 상위클래스는 들어가는 방법이없다. 자식->부모 (super) , 자식->자식(this) 나머지는 존재하지 않는다. 
  • 참조변수가 어디를 바라보고 있느냐는 어떤기능을 사용할 수 있느냐, 어느기능까지 사용할 수 있느냐에 대해서 중요함. 
  • 어디를 바라보냐에 따라서 내가 사용할 수 있는 메소드가 달라진다는 것은 Object로 참조변수가 바라보도록 지정할 수 있지만, 그렇게 된다면 Car에 대한 속성은 사용을 못하는 것이다. 자식클래스에 대해 상위클래스는 사용할 수 없기 때문에 Car속성을 사용하고 싶다면 참조변수가 Car를 바라보게 만들어야한다는 것이다.

2. 배열로 다양한 자식타입의 객체를 저장할 수 있다. 배열에 저장되는 객체의 종류들을 제한할 수 있다. 

  • //각종 자동차 객체를 저장할 수 있는 배열 생성하기 
    Car[] carArray1 = new Car[5];
    carArray1[0] = new Palisade();
    carArray1[1] = new SUV();
    carArray1[2] = new Car();
    carArray1[3] = new RacingCar();
    carArray1[4] = new Palisade();
    //자동차 배열에다가 차량을 전부 담을 수 있음

    //각종 SUV객체를 저장할 수 있는 배열 생성하기 
    SUV[] carArray2 = new SUV[5];
    carArray2[0] = new SUV();
    carArray2[1] = new Palisade();
    carArray2[2] = new Palisade();
    //carArray2[3] = new Car();//오류 SUV클래스의 하위 클래스타입 객체가 아님
    //carArray2[4] = new RacingCar();//오류 SUV클래스의 하위 클래스타입 객체가 아님
    //배열에 어떤걸 담을지 조절이 가능함
  • 이렇게 코딩을 진행하였다. 확인을 해보면 한종류로 묶어서 표현할 수 있고, 

실제로 사용할 기능이 포함되어 있는 클래스 타입의 변수 혹은 배열 객체를 생성하면 특별한 형변환 없이 기능을 실행할 수 있다. 실행하고 싶은 기능이 어디 있느냐에 따라서 배의 타입을 달리해야한다. 

  • Car객체의 배열을 만들때마다 다 다른 객체들이 생성된다. 
  • 요구사항이 있다고 하자, 모든 자동차들의 출발기능, 정지기능, 운전기능을 실행하고 싶은데 , 모든자동차들(배열)의 요구사항 기능이 어디에 구현되어 있는지 확인을 한다 ---->위에 그려진것처럼 이 기능은 Car에 구현이 되어 있다. 
  • 향상된 For문을 사용하여 for(Car car : cars) {
  • car.start();
  • car.drive();
  • car.stop();} 으로 구현할 수 있다. 부모객체를 바라보는 참조변수이면서 자식 객체를 new 연산자로 만든 객체는 부모객체를 바라보고 있기 때문에 부모기능까지 사용할 수 있기 때문이다. 위의 배열을 그대로 사용하였다. 
  • 그렇다면 오버로드기능이있는 SUV는?? 오류가 나는 것을 제외하고는 위의 [3],[4]번 제외하고는 출력이 가능하다. 오버로드는 SUV에 구현기능이기 때문에 SUV[] suvs = new SUV[5]; 로 배열을 생성하고 입력을 해주고 
  • 향상된 for문으로 구현을 한다 for(SUV suv : suv) {suv.driveOffRoad();}로 구현해주면된다. 
  • 이를 보면 Car류는 RacingCar,SUV,Palisade가 같은 종류인 것이고, SUV류는 SUV, Palisade류라는 것을 확인 할 수 있다. 특정클래스를 상속받았다는 것은 그 종류가 되었다는 것이고, 배열의 경우 같은 타입의 변수를 담을 수 있다. 

instanceof 

생성된 객체가 클래스 형변환 되어서 관리하고 있지만, 그 객체를 사용할 때는 그 객체의 모든 기능을 사용할 수 있다 (강제형변환을 이용한다면) instanceof연산자를 사용하면, 참조변수가 참조하는 객체가 실제로 어떤 객체인지 조사할 수 있다. -->덧셈,뺄샘과 동일한 연산자이다. 

  • 참조변수 instanceof 클래스 명 -->참조변수가 참조하는 객체가 지정된 클래스 타입의 객체이면 true를 반환하고, 아니면 false를 반환한다. 
  • 사용하는 이유, 하위클래스를 지정하는 객체가 맞냐이다. 
  • 즉, 위의 그림의 c4의 경우 참조변수가 참조하는 것은 Car이다. 그러나 생성된 객체는 palisade인 것이다.
  • insanceof를 사용해보자  
  • Palisade의 경우 Car, SUV, Palisade 객체로 이뤄져있기 때문에 이 3개에 대해서는 true가 나오는 것을 확인할 수 있다. Car instanceof Palisade보다 Car instanceof SUV를 if문으로 우선두었다면, 그대로 true라서 멈춰버렸을 것이다. 이런식으로 if문에서도 더하위클래스면 먼저 위치해두는 것이 필요하다.
  • 이런식으로 말이다. 
  • instanceof는 어떤걸 참조하는지 알 수 있다. 지금까지 참조객체한 것을 전체적으로 true로 출력할 수 있다

이렇게 저번 포스팅에서 상속, 지금 클래스 형변환에 대해서 공부를 했다 

상속 + 클래스 형변환 ===== 다형성이라는 것이 발휘가 된다. SOLID에 대한 3가지도 출력이 될 것 이다. 확인해보자

지금까지 공부한내용은 배열에 각기능이 있는것을 할 수 있다. 각 기능은 실행되길 바란다면, Car를 바라보던 배열을 바라보던 꺼낸 객체가 뭔지를 알아야 실행이 가능했다. 무작위로 담겨있어도 instanceof로 객체를 확인하고 실행할 수 있었다. 

1. 상속관계클래스를 자식에서 부모의 메소드로 재정의하는 것이 가능했다. 물려받은 것은 자식타입에서 원하는데로 변경이 가능했다. Override 

2.상속관계는 클래스 형변환이 가능했다. 자식타입의 객체를 부모타입의 참조변수가 참조하고, 그 기능을 사용할 수 있었다. (강제형변환)을 사용한다면

3. 이렇게 사용하면서 코딩의 중복을 없앴다. 

4.특정한 클래스를 나의 상위클래스로 정할 수 있었다. 그것은 같은 종류가 된다는 것을 이용한다. 

  • 운전하는 기능을 가지고 있는 Car라면 상속받으면 그 기능을 사용하는 것이 가능했고, 같은 종류가 되었다. 같은 부모를 둔다는 것은 같은 type이 된다는 것이다. 

다형성(Polymorphisn)

    • 메세지를 보내는 클래스를 구현하였다. 
    • KakaoTalk의 경우도 메세지를 보내기 때문에 KakaoTalk extends message로 상속받고 안쪽에 있는 void sendmessage (String from, String to, String text)를 수행문만 다르게 구현하였다. 필드도 새롭게 구성하고, 생성자를 이용하여 입력받으면 출력이 되게 작성하였다. 
    • 그리고 이메일로 메세지를 보낼 수 있기 때문에 EmailMessage extends Message하고, 기능을 오버라이딩하였다. 
    • 그럼 메소드를 3개를 재정의 한 것이다. 
    • messengerApp에서는 이 3 클래스를 new 클래스를 이용하여 구현을 하였다.마지막은 Messanger에 대한 클래스 형변환을 하여 kakaoTalk을 생성하고 참조변수가 kakao의 부모인 messege를 바라보게 하였다. 출력결과 

 

OMG 클래스를 강제형변환 하지 않고 message를 바라보는 참조객체는 자식객체의 메소드를 사용하게 되었다. 

하위클래스에서 메소드가 재정의 된 경우, 형변환을 하지 않고, 부모클래스의 참조변수를 만든 뒤, 재정의된 메소드를 사용하기 위해서 참조하는 객체만 바꾼것처럼 사용하게 되었다!!!

메세지를 보내는 회사가 있다. 메세지는 일단 보내는 기능이 있기 때문에 어느 병원에서 뭘 보낼지 모르기 때문에 메세지 기능을 가지고 있는 최상위의 객체인 메세지 객체를 변수 선언하였다. 이 변수가 실행될 때 각기 다른 new로 생성된 객체의 재정의된 메소드가 실행되었다. 

  • 다형성은 실행하는 메소드는 동일하지만, 실제로 사용하는 객체가 달라지면 (문자, 카톡, 이메일 ) 실행의 결과가 다르게 발현되는 것이다. 실제로 연결된 객체에 따라서 다르게 발현된다. --->다형성
  • 다형성이란 1. 메소드를 재정의하고 2. 클래스를 형변환 하면 실행이 되는 것이다.
  • 우리가 핸드폰을 사용하는 것을 생각하자.  음악을 듣고 싶을 때 이어폰을 꽂아서 나만 들을 수 있게 하고, 블루투스 스피커를 꼽아서 큰소리로 들을 수 있다.  스피커만 바꾸면 기능이 실행되는 것을 음질이 좋지 않고 나만 들을 수 없다고 핸드폰을 바꾸지는 않는다. -->그냥 실제 꽂아놓은 객체의 기능이 실현된다는 것이다. 

is a 관계는 상속관계이다.

  • child is a parent
  • kakaotalk is a message
  • EmailMessage is a message
  • employee is a person
  • student is a person
  • Iphone is a smartPhone
  • Galaxy is a smartPhone

has a 관계란 의존관계 포함관계이다.

  • 필요한 기능이 구현된 객체를 포함한다. 
  • 클래스들은 자신이 필요로 하는 기능이 구현된 객체를 가진다. 
  • 내가 필요한객체가 연결 될 참조 변수가 필요하다. 
  • Phone has a camera
  • Phone has a Speaker
  • computer has a monitor
  • computer has a keyboard 
  • computer has a mouse
  • ------>연결할 수 있는 어뎁터가 있어야지 사용할 수 있다. -->자바에서는 참조변수, 멤버변수라고 한다. 
  • 메소드내에 new 연산자로 넣어 놓는다면, 계속 메소드에서 변수 선언하고 사라지고 이건 메모리를 낭비하는 일이다. 공구박스의 가위를 사고 사용하고 버리는 일. 
  • 차라리 필드내에 참조변수를 선언하고 지속적으로 사용하는 것이 객체내에 선언을하면 참조하지 않을 때까지 유지되고, 하나만 만들어두고 쭉 사용할 수 있다. 멤버변수로써 선언하면 포함 관계로 되기 때문에 연결 포트로 생각하고 사용할 수 있다. 그렇게 생긴 녀석만, 같은 종류만 연결이 가능하다. 
  • 무엇을 연결하는지는 데이터타입만 보면 알아볼 수 있다. 참조 변수는 연결포트의 역활, 데이터 타입은 어떤걸 연결할 지 선언하는 부분 
  • 연결할 때 HDML꽂는 선언 부분이 표준이 정해진 것처럼 데이터타입의 정의된 메소드와 동일해야한다. 그것을 메소드가 표준으로 잡혔다고 하는 것이다. 통일된 규격이 존재한다. 표준이 있다. 사용법이 동일해야한다. 메소드의 선언부가 동일해야한다. 
  • 사용법이 같다 = 부모의 메소드를 재정의한 것이다. 
  • 교체가능하다 = 이 프로그램의 확장성이 넓다. 
  • 다형성 = 사용법은 동일한데 연결된 객체에 따라서 다른 결과가 나온다. 
  • package example_09_16_polymorphisn;
    /**
     * 뉴스 구독을 신청한 사용자에게 최신 뉴스를 발송하는 서비스 클래스다. 
     * @author 김승희
     *
     */
    public class NewsService {

    /**
     * 최신 뉴스를 사용자에게 전달할 때 사용하는 Messanger류 객체를 참조하는 참조변수 
     */
    private Messanger messenger;// 필드에 참조변수를 선언하여, Messager를 포함관계로 만들었다. 사용가능하다.
    /**
     * 
     * 
     * 기본생성자 메소드이다. 
     */

    // 앞의코딩은 new메소드로 사용할 객체를 적어놓음 , 아직은 객체가 연결되어있지않은 상태 
    public NewsService() {}
    /**
     * Messenger류 객체의 주소값을 전달받아서 멤버변수에 대입하는 생성자 메소드다.
     * @param messenger
     */

    public NewsService(Messanger messenger) { 
    this.messenger = messenger;
    }
    /**
     * Messenger류 객체의 주소값을 전달 받아서 멤버변수에 대입하는 setter메소드다.
  • 이제 setter메소드를 이용하여 값을 변경할 수 있다. 
     * @param messenger
     */

    public void setMessenger(Messanger messenger) {
    this.messenger = messenger;
    }
    /**
     * 최신 뉴스를 구독자에게 발송한다.
     * @param from 뉴스보내는 곳 
     * @param to  뉴스 받는 곳
     * @param news 뉴스 내용
     */
    public void broadcastNews(String from, String to, String news) {
    messenger.sendMessage(from, to, news);//messenger에 아직 따로 안넣어놔서 null오류가 생성됨.
    }
    }
  • 이용하는 App
  • package example_09_16_polymorphisn;

    public class NewsServiceApp {

    public static void main(String[] args) {
  • //사용하기 위해서 new 연산자를 이용하여 불러들일 수 있게 되었다. 카카오톡, 메세지, 이메일 클래스 
    Messanger messenger1 = new Messanger();
    KakaoTalk messenger2 = new KakaoTalk("홍길동", "고양이사진");
    EmailMessenger messenger3 = new EmailMessenger("admin","zxcv1234");

    NewsService service = new NewsService(); // 포함관계에 있는 newsService를 불렀다.
    //NewsService에 아무것도 안넣어 놨기 때문에 null오류가 남
    service.setMessenger(messenger1);//이렇게 넣어줘야함., 조립의 형식
    //service.setMessenger(messenger2);
    //service.setMessenger(messenger3);
    //newService가 결정하는 것이 아닌, App가 결정하도록 만들어짐 
    service.broadcastNews("010-1111-1111", "010-2222-2222", "코로나가 종식되었습니다.");
    }
    }
  • 만약 이것을 JVM에서 사용이 되지 않았다면 
  • Messeger message = new Messenger();//생성을 하고 
  • 실행방법은 sendMessege의 사용하는 객체가 달라졌을 것이고 
  • if, else if로 (Messeger instanceof Emailmesseger)로 형변환을 이용해서 객체가 맞으면 메소드가 실행되게 만들었어야했을 것이다. 
  • 물론 재정의 관계가 아닌 관계를 사용하려면 instanceod연산자를 이용하여 작성을 해줘야한다.
  • 그렇다면 코드는 더더더더욱 복잡해진다. 
  • 객체가 많아지면 더더더더더더욱 복잡해진다. 
  • 재정의 관계는 메소드 사용방법이 동일하기 때문에 한줄로 끝나버린다. 

객체의 획득방법은 3가지가 있다. 

  1. 내가 직접 누에고치에서 실을 뽑아서 직접만드는 것
  2. 백화점에서 내가 만들어진 옷을 찾아서 입는 것(LookUp)
  3. 친구나 가족이 사서 갖고오는 것 (이것은 편하다....-->DI : Dependency Injection) =>덜 번거로운 방법이다. 의존한다. 라는 의미이다.

SOLID의 원칙

  • S : SRP 단일 책임의 원칙 
    • Single responsibility Principle :한 클래스는 하나의 책임만 가져야한다. 
  • O : OCP : 개방 -폐쇄의 원칙
    • Open/Closed principle : 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야한다. 
    • 핸드폰에 8핀을 꽂으면 스피커, 충전기가 꽂힐수는 있지만(확장) 5핀,C핀을 꽂을수는 없다(변경)
  • L :LSP: 리스코프치환원칙
    • Liskov substitution Principle : 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위타입의 인스턴스로 바꿀 수 있어야한다.
    • 프로그램의 정확성 =>메소드의 변경, 메소드를 재정의하고, 하위타입의 인스턴스 : 상속해야한다  
  • D : DIP :의존관계 역전 원칙
    • DI : 획득하기 위해 제일 쉬운것은 얻는 것이다. 
    • 전제 조건 : 1. 상속관계여야한다. 2. 메소드가 재정의 관계여야한다. 

이런내용들은 Spring의 주요개념이다. DI의 구현체가 핵심기능이다. 

1.상속

2.메소드재정의

3.클래스 형변환

4.다형성  중요한 내용이다. 

 

반응형

'중앙 HTA (2106기) story > java story' 카테고리의 다른 글

추상화  (0) 2021.09.24
상속, 매개변수, 멤버변수, 반환타입의 다형성  (0) 2021.09.20
메소드 재정의 (Overriding)  (0) 2021.09.16
상속  (0) 2021.09.14
접근제한자 (modifier)  (0) 2021.09.13
Comments