JAVA/이론 정리 및 예제

[JAVA/자바] #9_1 다형성 / 예제

chaewon 2021. 9. 17. 18:36

타입

클래스가 곧 타입, 클래스가 청사진(설계도면)인데 사용하기 위해 heap에 만들라는 타입

다형성을 쓰는 이유

  • 두 클래스간의 의존관계를 줄이기 위해(TestA, TestB)
  • 연주자객체들을 객체배열을 이용해서 한 번에 연주시키기 위해

 

다형성*

  • ‘서로 다른 형태’ → 타입(자료형)이 다양하다
  • 상속으로 인해 파생된 기술이다. 
    • 자식클래스를 작성할때 부모클래스의 맴버뿐만아니라 타입또한 물려받는다.
    • 부모타입의 레퍼런스로 여러개의 자식객체들을 취급할 수 있는 것
    • 원클래스는 원이라는 타입이면서 도형이라는 타입을 가지고 있다
    • 삼각형 클래스는 삼각형 타입이면서 도형이라는 타입을 가지고 있다
    • 즉, 객체를 생성할때 도형이라는 레퍼런스 타입으로도 사용할 수 있다
    • => 상위타입의 레퍼런스하위 타입의 객체의 주소를 보관할 수 있다

 

클래스 형변환

    • up casting(JVM이 자동으로 추가해주는 코드이다.)
      • 부모타입의 레퍼런스는 모든 자식의 주소를 가질 수 있지만,
        자식 타입의 레퍼런스는 부모의 주소를 가질 수 없다
      • sonata는 Object이면서 sonata이면서 Car타입을 갖고있다
        • 하지만 반드시 sonata는 Car와 상속관계에 있어야한다
        • sonata에 의한 산출물을 상위의 타입에 대입하는 것 → 자동형변환

  • down casting(명시적 형변환)
    • (자식객체의 주소를 받은 부모) 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 후손 클래스 타입으로 참조형 변수를 형 변환해야 한다
    • c라는 레퍼런스는 sonata 객체를 가지고 있지만, 타입이 Car이다
    • c.moveSonata(); 메소드를 호출할때 c는 car타입(car, sonata, avante를 다 갖고 있다.)이기때문에 메소드가 있는 객체타입으로 임시적으로 형변환을 해주어야 sonata에 있는 sonatamove로 찾아갈 수 있다. ((Sonata)c).moveSonata();
      • 소나타가 아니라 다른 객체로 형변환을 하면 클래스캐스트에러!!
    • 레퍼런스 주소를 참조해서 사용을 하는건데 어디에 있는지 알기위해선 sonata의 형변환을 해줘야한다

 

객체배열과 다형성

  • 다형성을 이용하여 상속관계에 있는 여러 개의 자식클래스를 부모 클래스의 배열에 저장 가능
  • 따로 객체를 만들어서 사용하는 것 보다 훨씬 더 효율적이다.

 

매개변수와 다형성

  • 메소드 호출시 다형성을 이용하여 부모타입의 매개변수를 사용하면, 자식타입의 객체를 받을수 있다.

 

instanceof 연산자

  • 비교하는 레퍼런스와 클래스타입과 일치하는지 확인할 때 사용
public void method(Car c){
  if(c instanceof sonata){
    ((sonata)c).moveSonata();  //참일때 실행
  }
  //다운캐스팅의 예외처리 : false일때 실행하지 않고 빠져나와서 down casting이 일어나지 않는다.
}

method(new Sonata);
  • 해당타입에 맞춰 동작할 수 있도록 하기 위해서 사용한다

instanceof와 down-casting을 활용한 처리

 

바인딩

  • 실제 실행할 메소드 코드와 호출하는 코드를 연결시키는 것을 바인딩이라고 한다.

 

동적바인딩*

  • 오버라이딩된 메소드가 먼저 실행된다.
  • 컴파일시에 (소스코드와 메소드 헤드부분이 모두 연결되어 있다.) 실행할 당시의 객체 타입이 담기는데 그 기준으로 바인딩 되는 것을 동적 바인딩이라고 말한다.

 

동적바인딩 성립 요건

  • 상속 관계로 이루어져 다형성이 적용된 경우에, 메소드 오버라이딩이 되어 있으면 정적으로 바인딩 된 메소드 코드보다 버라이딩 된 메소드 코드를 우선적으로 수행하게 된다.
  • 실행할 당시의 객체 타입에 맞춰 수행한다.

  • 부모 클래스에 move 메소드가 있으면 다운캐스팅을 하지 않아도 된다.
    하지만, move메소드가 없다면 컴파일에러 발생

 

추상클래스

  • 몸체 없는 메소드(abstract가 있는 메소드)를 포함한 클래스
  • 추상 클래스일 경우 클래스 선언부에 abstract 키워드를 사용

추상메소드

  • 몸체({}) 없는 메소드를 추상 메소드라고 한다
  • 추상 메소드의 선언부에 abstract 키워드를 사용한다
  • 상속시, 반드시 구현해야 하는 메소드이다. (오버라이딩 강제화 해줄 목적)

추상클래스의 특징

  • 미완성 클래스(abstract 키워드 사용)
  • abstract 메소드가 포함된 클래스 -> 반드시 abstract 클래스
  • 자체적으로 객체 생성 불가 -> 반드시 상속하여 객체 생성
  • 일반적인 메소드, 변수도 포함할 수 있다
  • abstract메소드가 없어도 abstract 클래스 선언 가능하다.
  • 객체 생성은 안되나, ref변수 type으로는 사용 가능하다.

추상클래스의 장점

  • 일관된 인터페이스 제공
    • 부모클래스를 쓰려고 하는 사람과 자식클래스를 쓰려고 하는 사람의 중간 다리 역할을 해서 틀로써 하기위해 playManagement
  • 꼭 필요한 기능 강제함
    • (공통적이긴 하나, 자식클래스에서 특수화되는 기능)
      • cry() 닭은 꼬끼고 호랑이는 어흥 → 특수화

 

인터페이스

  • 상수형 필드와 추상 메소드만을 작성할 수 있는 추상 클래스의 변형체이다
  • 강제성 부여 → 공통적인 틀을 작성할 수 있게 
  • 메소드의 통일성을 부여하기 위해서 추상 메소드만 따로 모아 놓은것
    • 관련 없는 클래스들을 하나의 상위타입의 역할로 묶고 싶을때 인터페이스 사용

인터페이스 특징

  • 모든 인터페이스의 메소드는 묵시적으로 public이고 abstract이다.
  • 전역변수는 묵시적으로 public static final이다.
    • → 따라서 인터페이스 변수의 값 변경 시도는 컴파일시 에러를 발생
  • 객체 생성은 불가, 참조형 타입으로서는 가능하다
import java.io.Serializable;
import java.util.Comparator;

//추상메소드만 맴버로 가질 수 있는 추상클래스를 인터페이스라고 한다.
//상수 필드만 맴버로 추가할 수 있다.

//인터페이스간 상속은 extends를 사용하며 다중 상속을 지원한다.
//클래스에서 인터페이스 상속 시 implements 키워드를 사용하지만
//인터페이스간 상속은 implements 사용이 불가능하다.
public interface IProduct extends Serializable/*, Comparator*/
                /*implements Iterator*/{
        //상수필드는 반드시 선언과 동시에 초기화가 되어 있어야 한다.
        //모든 필드는 묵시적으로 public static final이다.
        /*public static final */String Product_NAME = "상품명";
        
        //인터페이스 안에 선언된 메소드는 모두 추상메소드이기 때문에
        //선언시 public abstract를 생략할 수 있다.
        //따라서 후손은 오버라이딩시 반드시 public으로 해야 한다.
        /*public abstract void agstImethod();*/
        void abstImethod();
        
        //인터페이스 내에서는 몸체 있는 메소드 작성이 불가능하다.
        /*void method() {}*/
        
        
}

인터페이스 장점

  • 상위 타입의 역할로 다형성을 지원하여 연결해주는 역할 수행
    • 상속을 받을때 임플리먼츠라는 키워드 사용
  • 해당 객체가 다양한 기능을 제공시에도 인터페이스에 해당하는 기능만을 사용하게 제한할 수 있다.
  • 공통 기능상의 일관성 제공 / 공동 작업을 위한 인터페이스 제공 => 행위(메소드)의 강제화

 

인터페이스 상속

  • Interface는 다중 상속을 지원하며 class가 상속을 받을 때에는 implements라는 키워드를 사용한다.
  • Interface간의 상속시에는 extends 키워드 사용 가능하고, 다중상속을 지원한다.
  • Interface 이용하여 단일 상속의 제한점을 극복할 수 있다.
[접근제한자] interface 인터페이스명
     extends 부모인터페이스명, 부모인터페이스명, …{}

[접근제한자] class 클래스명 extends 부모클래스명 
    implements 부모인터페이스명, 부모인터페이스명, …{}

인터페이스끼리의 상속은 다중상속을 지원

import java.io.Serializable;

//인터페이스 상속의 경우 implements 키워드를 사용한다.
//extends 키워드는 단일 상속만 지원하지만,
//implements 키워드는 다중 상속도 지원한다.
public class Book extends Product/*, Date*/ implements IProduct, Serializable{
        public Book() {}
        
        public void printBook() {
                System.out.println("Book 클래스의 printBook() 실행...");
        }

        @Override
        public void abstMethod() {}

        //인터페이스의 추상메소드는 오버라이딩 시 반드시 public으로 접근제한자를 설정해야 한다.
        @Override
        public void abstImethod() {}
        
        @Override
        public String toString() {
                return "Book 클래스의 toString() 메소드 실행...";
        }
        
        
}

 

인터페이스 VS 추상클래스*

  • 인터페이스는 abstract라는 추상클래스로 작성해야한다.
  • 모든 메소드는 묵시적으로 추상메소드이다
  • 단. 추상클래스는 반드시 명시해야함