티스토리 뷰

  1. 다형성이란

    • 여러 가지 형태를 가질 수 있는 능력

    • 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현

    • 즉, 조상클래스의 타입 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다.

    • 부모 클래스의 레퍼런스 변수는 자식 클래스의 타입 변수를 가져올 수 있다.

    • 그러나, 부모 클래스의 레퍼런스 변수는 부모 클래스 타입으로 되어있기 때문에 실제 인스턴스가 자식꺼라고 하여도 자식의 맴버 변수나 메서드에 접근할 수 없다.

    • 다만, 부모 클래스의 맴버는 사용가능하다.

    • 즉, 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 맴버의 개수가 달라진다.

    • 그런데, 반대는 불가능하다. 자식 클래스 레퍼런스에 부모 클래스의 인스턴스를 넣을 수 없다.

      • 이 경우에는 컴파일 에러가 된다.
    • 참조변수의 형변환

      • 참조변수도 형변환이 가능하다.

      • 업 캐스팅 : 자손 타입의 참조변수를 조상 타입의 참조변수로 변환하는 것 ( 형변환 생략가능 -> 누군지 알기 때문)

      • 다운 캐스팅 : 조상 타입의 참조변수를 자손 타입으로 변환하는 것 (형변환 생략불가 , 부모입장에서 자식을 모르기 때문)

      • 참조 변수간의 형변환은 캐스트 연산자로 () 안에 타입을 써주면 된다.

        Car car  = null;
        FireEngine fe = new FireEngine();
        car = fe; // car = (Car) fe; 로 형변환이 생략된 것이다.
        fe = (FireEngine)car; // 생략불가 
      • 주의 : 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것이 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

        • 단지 참조변수의 형변환을 통해 참조하고 있는 인스턴스에서 사용할 수 있는 맴버의 범위(개수)를 조절할 뿐이다. 이는 다음의 문제와 헷갈려서는 안된다. 조상 인스턴스를 가진 조상 레퍼런스 변수를 자식 레퍼런스 변수로 변환하더라도 사용할 수 없다. 에러가 발생할 수 밖에 없다. 이유는 레퍼런스 변수의 타입만 바꾼것이지 인스턴스를 바꾼 것은 아니기 때문이다. 이 때에는 컴파일은 성공하지만 실행 시 ClassCastException이 발생한다. 자식 -> 부모의 형변환으로 메서드를 불러오는 것은 가능하지만 자식 레퍼런스 변수로 부모를 가져올 수 없다는 것은 인지하자
    • 그래서 부모타입을 자식타입으로 캐스팅하는 것이 불가능하므로 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는 것이 좋다.

    • instanceof 로 인스턴스의 참조형 변수를 검사한 타입으로 변환할 수 있는 지 확인할 수 있다. 결과값은 true or false 이다. (인스턴스 타입과 레퍼런스 변수 타입은 다를 수 있다.)

      void doWork(Car c){
        if(c instanceof FireEngine){
            FireEngine fe = (FireEngine) c;
            fe.water();
        }    
      }
    • 인스턴스만 자식 클래스이기만 하면 소용없다. 그러면 부모의 레퍼런스 변수로 접근할 수 있는 영역이 한계가 있기 때문이다.

    • 그래서 형변환을 하는 것이다.

    • 만약 직접 인스턴스의 타입을 확인하고 싶다면 .getClass().getName() 을 하면 참조변수가 가리키고 있는 인스턴스 클래스 이름을 문자열로 반환한다.

  2. 참조변수와 인스턴스의 연결

    • 만약 조상 클래스와 자손 클래스에 이름이 같은 맴버 변수가 있다면 누구를 호출할까??
    • 레퍼런스 변수가 조상 타입이면 조상 클래스에 선언된 맴버 변수가 사용되고, 자손 타입의 참조 변수를 사용할 때는 자손 클래스에 선언된 맴버 변수가 사용된다.
    • 만약 자손 클래스에서 부모 클래스의 데이터를 쓰고 싶다면 super.x 를 사용하고, 자손에서 자신의 데이터를 불러오고 싶다면 this.x를 사용하자
  3. 다형성을 이용하여 하나의 타입(부모의 타입)으로 자식들의 인스턴스를 담을 수 있어, 부모타입 배열이나 매개변수를 선언하여 값을 받아낼 수 있다.

  4. 인터페이스의 장점

    1. 개발시간을 단축
    2. 표준화가 가능
    3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
      • 상속 관계가 아니어도 composite한 관계를 맺으면 되기 때문이다.
    4. 독립적인 프로그래밍이 가능하다.
      • 선언과 구현을 분리하여 독립적으로 프로그래밍 구현가능, 이는 구현부의 변경이 다른 클래스에 영향을 주지 않는 다는 것이다.
  5. 인터페이스의 이해

    • 클래스를 사용하는 쪽(User)와 클래스를 제공하는 쪽(Provider)이 있다.
    • 메서드를 사용하는 쪽에서는 사용하려는 메서드의 선언부만 알면 된다. (내용은 몰라도 된다. information hiding)
  6. 디폴트 메서드와 static 메서드( jdk 1.8 이후)

    • static 메서드는 인스턴스 관계가 없는 독립적인 메서드이다.

    • public으로 적어야 하며, 생략가능하고 구현을 적어야 한다.

    • 만약 인터페이스가 변한다면 어떻게 될까?? 구현을 하는 모든 클래스들은 새로 추가된 메서드에 대한 구현을 해야할 것이다.
      이에 따라, 자바 개발자들은 구조에 영향을 안주고 abstract 추상 메서드가 아닌 메서드를 추가할 수 있도록 하였다.
      이것이 디폴트 메서드이다.

    • 디폴트 메서드는 추상 메서드의 기본적인 구현을 제공하며, 추상 메서드가 아니기 때문에 디폴트 메서드가 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

    • 앞에 키워드 default를 붙이며, 추상 메서드와 달리 일반 메서드처럼 몸통이 있어야 한다. 접근제어자는 public이다.

```java
interface MyInstance {
    void method();
    default void newMethod(){}
}
```

-   다만, 디폴트 메서드가 기존의 메서드와 이름이 중복되어 충돌하는 경우가 발생한다. 이를 해결하는 규칙은 다음과 같다.

    1.  여러 인터페이스의 디폴트 메서드 간의 충돌
        -   디폴트 메서드를 오버라이딩 해야한다.
    2.  디폴트 메서드와 조상 클래스의 메서드 간의 충돌
        -   조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.

    \-> 그냥 필요한 기능으로 오버라이딩해놓으면 된다.

    -   참고 코드

    ```java
    class DefaultMethodTest{
       public static void main(String[] args){
           Child c = new Child();
           c.method1();
           c.method2();
           MyInterface.staticMethod();
           MyInterface2.staticMethod();
       }
    }

    class Child extends Parent implements MyInterface, MyInterface2{
       public void method1(){
           System.out.println("method1() in Child"); // 오버라이딩
       }
    }

    class Parent{
       public void method2(){
           System.out.println("method2 in parent");
       }
    }

    interface MyInterface{
       default void method1(){
           System.out.println("method1 () in MyInterface");
       }
       default void method2(){
           System.out.println("method2() in MyInterface");
       }
       static void staticMethod(){
           System.out.println("staticMethod() in MyInstance");
       }
    }

    interface MyInterface2{
       default void method1(){
           System.out.println("method() in MyInterface2");
       }
       static void staticMethod(){
           System.out.println("staticMethod() in MyInterface2");
       }
    }
    ```
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함