티스토리 뷰

스프링, 자바

자바 상속

killog 2020. 12. 26. 19:33
반응형

목표

자바의 상속에 대해 학습하세요.

학습할 것 (필수)

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

마감일시

2020년 12월 26일 토요일 오후 1시까지.


자바 상속의 특징

  • 부모의 클래스에 선언되어있는 publicprotected 로 선언돼어있는 모든 변수와 메소드를 내가 가진 것 처럼 사용할 수 있다.

  • 상속을 활용함으로써, 이중 삼중의 일의 반복을 줄일 수 있다.

  • 자바의 경우, extends 뒤에는 하나의 클래스, 단일 상속만 가능하다.

  • 잘 만들어놓은 클래스에서 파생시켜서 추가적인 기능을 넣는다고 생각하자.

  • 사용 방법

    extends

    public class Child extends  Parent {
      ...
    }
  • 예시 코드

    package study.whiteship;
    
    public class Parent {
        public Parent(){
            System.out.println("parent Consturctor");
        }
        public void printName(){
            System.out.println("Parent printName()");
        }
    
    }
    
    package study.whiteship;
    
    public class Child extends  Parent {
        public Child() {
            System.out.println("Child Constructor");
        }
    }
    
    package study.whiteship;
    
    public class Main {
    
        public static void main(String[] args) {
            Child newChild = new Child();
            newChild.printName();
        }
    }
    

    결과물

  • $![image-20201225130839466](/Users/kil/Library/Application Support/typora-user-images/image-20201225130839466.png)

​ Parent 클래스의 메소그를 호출하지도 않았는데, 확장을 한 클래스가 생성자를 호출하면, 자동으로 부모 클래스의 기본 생성자가 호출된 것을 확인할 수 있다.

Q.부모만 상태를 가지고 기본 생성자 따윈 없다면? 부모클래스에 매개변수를 받는 생성자만 있으면 컴파일 에러가 나는가?

package study.whiteship;

public class Parent {
    String name;
    public Parent(String name) {
        this.name = name;
        System.out.println("parent Consturctor: " + name);
    }
    public void printName() {
        System.out.println("Parent printName()");
    }

}

child 에 매개변수를 넣어줘도 안되는가?

안된다. 부모에게 인자가 있는 경우 super 를 무조건 삽입을 해줘야한다.

이 현상을 해결하기 위해선,super 함수를 이용해, Parent 의 매개변수를 충족시키는 방식으로 해결 할 수 있다.

:: 결론
  • 부모클래스에 매개변수 없는 기본생성자만 있거나
  • 자식 클래스에서 부모클래스의 생성자를 명시적으로 지칭하는 super() 을 사용한다.
package study.whiteship;

public class Child extends Parent {
    public Child() {
        super("parentTmp");
        System.out.println("Child Constructor");
    }
}

Q.부모만 생성자가 여러개가 있을때 super(null) 을 호출하면 누가 호출이 되는가?

A. 모르겠다.

책에서 나오기로는 에러가 나와야하는데, 똑똑한 intellyJ 는 자기가 알아서 인자를 넣어버린다.

둘 중에 자기가 원하는 결과로 나오는 것 같다.

package study.whiteship;

public class Parent {
    String name;
    Object object;

    public Parent(Object object) {
        this.object = object;
        System.out.println("parent Consturctor:object: ");
    }

    public Parent(String name) {
        this.name = name;
        System.out.println("parent Consturctor:name: " + name);
    }


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

}
package study.whiteship;

public class Child extends Parent {
    public Child() {
        super(null);
        System.out.println("Child Constructor");
    }
}

![image-20201225134838060](/Users/kil/Library/Application Support/typora-user-images/image-20201225134838060.png)

super 키워드

  • 자식 클래스를 컴파일할 때, 자동으로 super() 라는 문장이 들어간다.

  • 부모클래스의 생성자를 호출하는 super() 은 반드시 자식 클래스의 생성자의 첫 줄에 호출되어야한다.

  • super() : 부모 생성자

  • super : 부모 클래스

  • super.printName() : 부모클래스의 메소드 사용

  • ![image-20201225133639939](/Users/kil/Library/Application Support/typora-user-images/image-20201225133639939.png)


[uml 표현]

메소드 오버라이딩

자식 클래스에서 부모 클래스에 있는 메소드와 동일하게 선언하는 것을 메소드 오버라이딩 이라고한다.

효과: 부모 클래스에 선언되어있는 메소드와 동일하게 선언되어 있는 메소드를 자식 클래스에 선언하면, 자식 클래스의 메소드만 실행된다.

((자식으로 갈수록 public) 접근 제어자, 리턴타입 과 시그니처(메소드이름 , 매개변수 타입 및 개수)가 동일해야 메소드 오버라이딩이라 부른다.

생성자에서는 자동으로 부모 클래스를 생성하는 super() 생성자가 호출되지만, 메소드 오버라이딩은 그렇지 않다.

오버라이딩(overriding) 의 사전적 의미

다른 무엇보다 더 중요한, 최우선시 되는

cf. 메서드 오버로딩 (로화라덮이라 암기)

  • 오버로딩(overloading): 확장,메소드의 매개변수들을 확장
  • 오버라이딩(overriding): 덮어쓴다.(부모 클래스의 메소드 시그니처를 복제하여 자식 클래스에서 새로운 것을 만들어 내어 부모 클래스의 기능을 무시하고,자식 클래스에서 덮어씀)
메소드 오버로딩이란?

오버로딩은 메소드와 리턴값은 같지만 매개변수가 다른 것을 의미한다. 만약 매개변수의 타입이 달라져도 오버로딩이 성립된다.

public class A {
     public void print(){
           System.out.println("A");
     }

     public void print(int a){
           System.out.println("A" + "_" + a);
     }

     public void print(int a, int b){
           System.out.println("A" + "_" + a + "_" + b);
     }
}

정석적인 메소드 오버라이딩

package study.whiteship;

public class Main {

    public static void main(String[] args) {
        Child newChild = new Child();
        newChild.printName();
    }
}
package study.whiteship;

public class Parent {
    public Parent() {
        System.out.println("parent Consturctor");
    }

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

}
package study.whiteship;

public class Child extends Parent {
    public Child() {
        System.out.println("Child Constructor");
    }
    public void printName() {
        System.out.println("Child printName()");
    }
}

위의 코드를 기본으로 변형을 해보자.

Q.그렇다면 자식에서 같은 메소드명, 다른 리턴값을 가진 친구가 있어도 되는가?

public String printName() {
    System.out.println("Child printName()");
}

알아서 오버라이드라 인지를 하고 리턴값이 다르다고 욕한다.

Q. 자식에서 다른 매개변수는 어떠한가?

  public void printName(String name) {
        System.out.println("my name:" + name);
        System.out.println("Child printName()");
    }

A. 메소드 오버로딩으로 인지되어 다른 함수라 인지돼, 오버라이딩이 발생하지 않았다.

Q. 여기서 끝낼 매개변수 인가?

A. 대츠 노노, 그렇지 않다. 가변 매개변수 ... 을 사용해서 인텔리 제이 몰래 비슷해 보이는 상황을 만들어줘보았다.

package study.whiteship;

public class Parent {
    public Parent() {
        System.out.println("parent Consturctor");
    }

    public void printName(String... names) {
        for (String name : names) {
            System.out.println("Parent printName():" + name);
        }
    }

}
package study.whiteship;

public class Child extends Parent {
    public Child() {
        System.out.println("Child Constructor");
    }
    public void printName(String name) {
        System.out.println("Child printName():" + name);
    }
}

이 결과 놀라운 성과가 나왔다. 가변 매개변수를 어디에 사용하냐에 따라, 가변 매개변수 외의 상황으로 출력이 되었다.

가변 매개변수가 항상 후순위의 순서를 가지고 있다는 것을 알게 되었다.

Q. 자식에서 다른 접근제어자는 어떠한가 접근제어자가 같아야 메소드 오버라이딩인가?

A. 대츠 노노, 그렇지 않다. 다를 수 있다. 단, 부모에 비해 접근 권한이 확장되는 경우만 가능하다.

  • 사전지식

    • 접근 제어자는 접근 권한이라 보면 된다.
    • public> protected > package-private> private ( 오른쪽으로 갈수록 접근 권한이 좁아진다. )
     private void printName() {
          System.out.println("Parent printName():");
      }

​ 반대의 경우 이런 에러가 나오는 것을 볼 수 있다.

Q. 엄마 메소드 ( super 와 유사한 ) 를 다시 쓸 방법은 없는가?

A. 당연히 있다. 우리가 사용했던, super 를 잘 이용하면 된다. 생성자처럼, 아무것도 지정하지 않아도 자동으로 super() 을 이용하는 것은 아니지만, 우리가 직접 지정하면 사용가능하다.

   public void printName() {
        super.printName();
        System.out.println("Child printName():");
    }

놀랍게도, 꼭 super 의 위치가 최상단일 필요 또한 없다.

  public void printName() {
        System.out.println("Child printName():");
        super.printName();
    }

참조 자료형의 형변환

  • 참조 자료형도 형변환이 가능하다.
  • 자식 타입의 객체를 부모 타입으로 형변환하는 것은 자동으로 된다.
  • 부모 타입의 객체를 자식 타입으로 형변환할 댸는 명시적으로 타입을 지정해줘야한다. 이때, 부모 타입의 실제 객체는 자식타입이여야한다.
  • instanceof 예약어를 사용하면 객체의 타입을 확인할 수 있다.
  • instanceof로 타입을 확인할 때, 부모 타입도 true 라는 결과를 제공한다.

이번 예제로는 Parent 와 Child 를 버리고 새로운 예제를 써보자..

인텔제이에서는 mac 기준 command + N 에서 generate function 으로 constructor 및 overriding function 을 만들 수 있다.!

  • 새로운 예제
package study.whiteship;

public class ParentCasting {
    public ParentCasting() {
    }
    public ParentCasting(String name) {
    }
    public void printName() {
        System.out.println("printName() - parent");
    }
}
package study.whiteship;

public class ChildCasting extends ParentCasting {
    public ChildCasting() {
    }

    public ChildCasting(String name) {
        //  super(name);
    }

    @Override
    public void printName() {
        System.out.println("printName() - Child");
    }

    public void printAge() {
        System.out.println("printAge() - 18 months");
    }
}
public class Main {

    public static void main(String[] args) {
        Main main = new Main();
        main.objectCast();
    }

    public void objectCast() {
        ParentCasting parentCasting = new ParentCasting();
        ChildCasting childCasting = new ChildCasting();
        ParentCasting parentCasting1 = childCasting;
        ChildCasting childCasting1 = (ChildCasting) parentCasting; // 에러 발생
        //class study.whiteship.ParentCasting cannot be cast to class study.whiteship.ChildCasting 

    }

}
package study.whiteship;

public class Main {

    public static void main(String[] args) {
        Main main = new Main();
        main.objectCast();
    }

    public void objectCast() {
        ParentCasting parentCasting = new ParentCasting();
        parentCasting.printName();
        ChildCasting childCasting = new ChildCasting();
        childCasting.printName();
        ParentCasting parentCasting1 = childCasting;
        if (parentCasting1 instanceof ChildCasting) {
            System.out.println("Q.parentCasting1 is still instance of ChildCasting:: True ");
        } else {
            System.out.println("Q.parentCasting1 is still instance of ChildCasting:: False");
        }
        parentCasting1.printName();
        // parentCasting1.printAge();
        //  ChildCasting childCasting1 = (ChildCasting) parentCasting; // 에러 발생
        //class study.whiteship.ParentCasting cannot be cast to class study.whiteship.ChildCasting
        ChildCasting childCasting2 = (ChildCasting) childCasting; // 명시적이라 하지만, 딱히 인텔리제이에서 형을 지정안해줘도 에러 안남.
        childCasting2.printName();
    }

}

Q.다운 캐스팅, 업캐스팅 되면, 오버라이딩 된 메소드 중에 어떤 메소드가 불리는가?

:: polymorphism (다형성)

-> 무조건 처음 생성자. 즉, 아들의 메소드가 불린다.

  • A. 형 변환을 하더라도, 실제 호출된ㄴ 것은 원래 객체에 있던 메소드가 호출된다.

  • package study.whiteship;
    
    public class Main {
    
      public static void main(String[] args) {
          Main main = new Main();
          main.objectCast2();
      }
    
      public void objectCast2() {
          ParentCasting parentCasting = new ParentCasting();
          ParentCasting parentCasting1 = new ChildCasting();
          ParentCasting parentCasting2 = new ChildOther();
          parentCasting.printName();
          parentCasting1.printName();
          parentCasting2.printName();
    
      }
    }
    

JAVA ORACLE

  • 생성자
    • 자식 클래스의 생성자가 호출되면, 자동으로 부모 클래스의 매개 변수가 없는 기본 생성자가 호출된다. 명시적으로 super() 이라 지정할 수 있다.
    • 부모 클래스의 생성자를 명시적으로 호출하려면, super() 를 사용하면 된다.
  • 변수
    • 부모 클래스에 private로 선언된 변수를 제외한 모든 변수가 자신의 클래스에 선언된 것 처럼 사용할 수 있다.
    • 부모 클래스에 선언된 변수와 동일한 이름을 가지는 변수를 선언할 수 있다. 다만, 권장하지 않는다.
    • 부모 클래스에 선언되어 있지 않은 이름의 변수를 사용할 수 있다.
  • 메소드
    • 변수처럼 부모 클래스에 선언된 메소드들이 자신의 클래스에 선언된 것처럼 사용할 수 있다.
    • 부모 클래스에 선언된 메소드와 동일한 시그니처를 사용해 메소드를 overrriding 할 수 있다.
    • 부모 클래스에 선언 안된 이름의 메소드를 선언할 수 있다.

메소드 디스패치 (Method Dispatch)

메소드 디스패치는 어떤 메소드를 호출할지, 결정하여 실제로 실행시키는 과정을 의미한다. 이러한 메소드 디스패치에는 정적 메소드 디스패치(Static Method Dispatch), 동적 메소드 디스패치(Dynamic Method Dispatch), 더블 메소드 디스패치(Double Dispatch) 세 가지가 존재한다.

정적 메소드 디스패치(Static Method Dispatch)

우리가 아는 것과 같이 컴파일러 역시도 이 메소드를 호출시키고 실행시켜야하는 것을 명확하게 알고 있을때, 우리는 이것을 정적 메소드 디스패치라 한다. 위에서 했던, 오버라이딩, 오버로딩 예제가 이에 해당한다.

동적 메소드 디스패치(Dynamic Method Dispatch)

동적 메소드 디스패치는 컴파일러가 어떤 메소드를 호출할 지 모르는 것을 의미한다.

cㅣass A{
private BB bb;
  A(BB bb){
    this.bb = bb;
  }
  void print(){
    bb.print();
  }
}


class B implements BB{
  public void print(){
    System.out.println("B");
 }
}

class B1 implements BB{
    public void print(){
      System.out.println("B1");
    }
}

interface BB{ 
  void print();
}

}

위에 예제에서 BB라는 추상클래스는 B, B1 으로 각각 구현되고 있다. 또한, A라는 클래스는 이런 BB라는 추상 클래스(설계도)를 받아 bb.print() 라는 함수를 사용할 수 있다.

그렇다면 여기서 A 클래스의 print() 함수를 사용하면, 함수의 객체를 선언할때, 할당된 Object 를 보고 어떤 함수를 실행할 지 결정할 것이다. 즉, 컴파일러에서는 어떤 함수가 실행될지 모르고, 런타임 시점에서 함수가 결정되는 것을 동적 메소드 디스패치라 부른다.


추상 클래스

추상 클래스는 클래스를 만들기 위한 일종의 설계도로 인스턴스를 생성할 수 없는 클래스이다. 사용하기 위해서는 반딋 자식 클래스에서 상속받아 클래스를 모두 구현해야한다.

// 추상 클래스 선언 방법
abstract class 클래스이름{

}

추상 클래스는 반드시 하나 이상의 추상 메서드를 포함하고 있고, 생성자와 멤버 변수, 일반 메서드 모두를 가질 수 있다.

  1. 자체 인스턴스 생성 불가능
  2. 생성자와 멤버변수, 일반 메서드 모두를 가질 수 있다.
  3. 하나 이상의 추상 메서드를 포함한다.

추상메서드(abstract method)

abstract 리턴타입 메서드이름();

추상 클래스는 메서드의 선언부만 작성하고, 구현부는 미완성인채로 남겨두는 메소드를 말한다. 추상 클래스는 보통 주석을 통해 어떤 기능을 수행하는지 알려주고, 구현부는 각각 상속받는 자식클래스마다 다르게 구현된다.

  1. 메서드의 선언부만 작성하고, 구현부는 미완성이다.
  2. 자식 클래스는 반드시 추상 메서드를 구현해야하고, 만약 구현하지 않을시, 자식 클래스도 추상 클래스가 돼야 한다.
  3. 추상 메서드의 접근 지정자에는 private 를 사용할 수 없다.

인터페이스(interface)

추상 클래스의 일종으로 추상화정도가 높아 일반 메서드나 멤버 변수를 가질 수 없다. 오로지 추상 메소드나 상수만을 멤버로 가질 수 있다.

interface 인터페이스명{
...
}
interface B{...}
interface C{...}
interface D{...}
class k{}

//인터페이스는 다중 상속이 가능
interface A extends B, C, D{
} 

//구현한다는 의미로 사용
class A extends K implements B, C{..}

인터페이스의 모든 멤버 변수는 public static final이어야 하며, 이를 생략할 수도 있다. 또한 메소드 역시 모두 public abstract이어야 하며, 이를 생략 가능하다.

이런 인터페이스는 같은 인터페이스끼리만 상속이 가능하며, 상속과는 다르게 다중 상속이 가능하다.


final 키워드

엔티티를 한 번만 할당하겠다는 의미로 3가지 의미를 가진다

  • final 변수
    • final 변수는 우리가 아는 상수를 의미한다. 생성자나 대입연산자를 이용해 한 번만 초기화가 가능한 변수이다.
  • final 메소드
    • final 메소드는 오버라이드하거나 숨길 수 없음을 의미한다.
  • final 클래스
    • 해당 클래스는 상속할 수 없음을 의미한다. 상속할 수 없기 때문에, 상속계층에서 마지막 클래스라는 의미.

Object 클래스

java.lang.Object 클래스는 모든 클래스의 최상위 클래스이다.

모든 클래스는 정의할 때부터 명시적으로 java.lang.Object 클래스를 상속 받게 된다. 그러므로 위에 정의된 Object의 함수들은 어떤 클래스에서도 호출이 가능하다.

https://blog.naver.com/swoh1227/222181505425


출처

자바의 신

https://blog.naver.com/swoh1227/222181505425

반응형

'스프링, 자바' 카테고리의 다른 글

자바의 인터페이스  (0) 2021.01.03
7주차 과제: 패키지  (0) 2020.12.27
java 의 collection  (0) 2020.12.24
JVM 에 대해서  (2) 2020.12.24
백기선 라이브 스터디 5주차 과제 클래스  (0) 2020.12.19
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
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
글 보관함