본문 바로가기
클라우드 활용 자바개발자 양성과정/01. JAVA 프로그래밍 언어 활용

08. 상속 Inherit & 09. 다형성 Polymorphism

by moca7 2024. 7. 3.

 

ㅁ 변수의 자료형이 boolean인 경우 getter 메서드 get을 안쓰고 is를 쓴다.

 

ㅁ 오버로딩

- 한 클래스 내에서 같은 이름의 메소드를 여럿 정의하는 것.

 

ㅁ 오버라이딩

- 반환형도 같아야 함. (오버로딩은 반환형 상관없음)

- 오버라이딩시 자식 객체의 메소드가 우선되고 부모 객체의 메소드는 숨겨진다. 

= 지금은 자바가 버전업되서, jdk버전업되면서 부모메소드 반환형의 자식타입까지는 가능.

버전이 안된다면 부모메소드의 반환형과 동일하게 해야한다. (거의 똑같이 할겁니다. 다르게 할 경우는 거의 없을것)

= 부모메소드의 접근제한자보다 범위가 같거나 커야한다.

- @Override 주석 붙이기 권장(jvm, 다른 개발자에게 오버라이딩된 메소드라느 ㄴ것을 알리기 위해)

(부모 메소드가 변경된 경우 - 자식클래스에 올 ㅠ발생 - 수정해야하는거알려줌

자식메소드로 작성시 실수로 잘못작성한 경우 - 오류 발생 - 수정해야하는ㄱ ㅓ알려줌)

@Override 어노테이션을 사용하면 자바 컴파일러가 해당 메소드가 실제로 부모 클래스나 인터페이스의 메소드를 오버라이딩하는지 확인할 수 있습니다.

 

 

ㅁ 지역변수에는 final만 붙일 수 있다.

 

ㅁ 여러개의 자식 클래스에서 추상화하여 부모 클래스를 설계할 때 만들어야 하느 ㄴ것들.

공통된 변수, 인포메이션, 게터세터, 매개변수없는생성자, 매개변수다있는생성자

ㅁ 자식클래스에서 해야하는 것들

- 없는 변수, 게터세터, 매개변수없는생성자

- 인포메이션은 자식클래스에서 오버라이딩.

+ 그리고 나중에 부모에서 가져온 변수4개를 합쳐서 총 5개의 매개변수를가진 생성자를 자식클래스에서만들고, 

거기에 super.setXXX로 대입하지말고,

부모클래스의 모든 매개변수생성자를 호출하면 더 간단. 

근데 이때 super.부모생성자() 형식이 아니고, super();로. 

+ 자식클래스에서 information메소드를 재정의할때 하나하나 getXXX로 해도 되지만, 부모클래스의 super.information()에다가 또 +로 이어붙이면 더 간단.

 

 

ㅁ 생성자를 호출하는 문장은 반드시 가장 위에 있어야 한다. 

 

ㅁ 상속관계

- 부모 자식, 조상 후손, 상위 하위, 슈퍼 서브, 

 

ㅁ 클래스와 클래스간에 다중상속 불가. 부모가 여럿일 수 없음.

- 인터페이스는 클래스와 클래스 간의 다중 상속을 지원하지 않는 Java에서 다중 상속을 구현하는 주요 방법입니다. 다중 상속이란 한 클래스가 여러 개의 클래스로부터 상속을 받는 것을 말합니다. Java에서는 클래스가 하나의 직접적인 부모 클래스만 가질 수 있지만, 인터페이스는 여러 개를 구현할 수 있습니다.

 

ㅁ 부모 클래스와 자식 클래스에 동일한 이름의 정적 메서드가 있을 경우, Java에서는 메서드 오버라이딩이 아니라 "메서드 숨기기"라는 개념이 적용됩니다.

- 정적 메서드의 경우 메서드 오버라이딩 개념이 적용되지 않기 때문에, 변수 타입이 부모 클래스이건 자식 클래스이건 정적 메서드는 변수가 선언된 클래스의 타입을 기준으로 호출됩니다.

 

ㅁ 자식 생성자에서 부모생성자가 호출된다는 것은, 부모 객체가 생성된다는 것이다.

(내부적으로 부모객체가 먼저 생성됨)

- 자식 생성자 호출보다 부모 생성자 호출이 항상 먼저고, 자식 객체 생성보다 부모 객체 생성이 항상 먼저다. 

 

ㅁ 기본생성자를 작성하는 습관을 들이라는 이유

- 부모 클래스의 기본생성자를 생략하고 매개변수생성자만 만들어놓은 경우에,

자식 생성자에서(기본생성자인지 매개변수있는생성자인지 상관없이) super(매개변수생성자)하지 않은 경우에는 자동적으로 super()를 호출해서 오류난다. 

- 내가 지금 만드는 클래스가 언제 부모생성자가 될 지 모름. 

 

- 자식 생성자는 항상 부모의 매개값없는 기본생성자를 호출하는 구문이 첫줄에 생략되어있다. 

 

ㅁ 

- # protected

- ~ default

- - private

- + public

- 기울이면 abstract다.

 

ㅁ 기본생성자에는 super()가 다 숨겨져있는데, 이 클래스가 상속받는게 없을경우 부모 객체를 생성하는데 문제되는거 아님? 

- 아님. 모든 클래스는 Obect 클래스를 상속받기 때문.

extends Object가 숨겨져있따. 

- 어떤 객체를 생성할 때마다 object 객체도 같이 생성된다. 필드는 없고 메서드만 있다. 

 

ㅁ 이클립스 오른쪽 클릭 - source - generate constructor using fields 하면 매개변수 내가 골라서 생성자 자동 완성가능. 

- 단축키 : 알트 쉬프트 s - o - 엔터

 

ㅁ 게터세터 빨리만들기

- 단축키 : 알트 쉬프트 s - r - 알트 a - 알트 r

 

ㅁ 그냥 println으로 객체를 호출하면, jvm이 자동으로 해당 레퍼런스.toString() 메소드를 반홯해서 그걸 출력함. 주소값.

- 레퍼런스(참조변수)를 출력할 때, JVM이 자동으로 레퍼런스.toString() 메소드 호출

 

ㅁ 상속을 배우기 전이었어서 information메소드로 별도로 만들었던 거고, 사실은 toString을 오버라이딩해서 그 객체의 멤버변수의 값들을 반환.

- 그래서 그냥 println 으로 객체 호출해서 함. 

 

ㅁ 객체 두개를 println에서 ==와 equals로 하면, 둘다 false나옴.

- ==는 애초에 값가지고 비교하는건데 참조자료형들은 각자의 주소값이 다르기 때문이고.

- equals도 오버라이딩 안되어있으면 ==로 되어있음. String처럼 오버라이딩해야함. 

 

- 오버라이딩전: object클래스의 equals메소드 실행 -> return 두 객체간의 주소값을 비교 ==;

- 오버라이딩후: book클래스의 equals메소드 실행 -> return 두 객체간의 모든 필드값들 비교;

(equals 메소드는 주소값을 비교해서 내용이 같아도 새로 생성되면 false를 반환하기 때문에 오버라이딩해서 필드값들 비교로 바꿈. 이때 String이면 equals메소드로비교)

 

ㅁ 자바에서 동일객체

public class Book {

 

    private String title;

    private String author;

    private int price;

}

 

Book b3 = new Book("자바", "홍길동", 300);

Book b4 = new Book("자바", "홍길동", 300);

// 동일한 데이터의 Book객체 2개 생성

 

 

- 자바에서 동일객체란 equals의 결과가 true이고, 각 객체의 hashCode 결과값이 일치하는 경우이다. 

@Override

public boolean equals(Object obj) {

 

    if(obj instanceof Book) {

        Book bbb = (Book) obj;

        if(this.title.equals(bbb.title) && this.author.equals(bbb.author) && this.price==bbb.price) {

            return true;

         }

        else {return false;}

     }

     else {

          return false;

      }

 

}

 

- 이렇게 equals를 오버라이딩 했어도, b3와 b4의 해시코드가 다르다. 고로 같은 객체는 아니다. 

그래서 hashcode를 재정의해서 필드가 다 같으면 같은 숫자가 반환되게끔 할 것임. 

 

- 오버라이딩전: object클래스의 hashcode메소드 실행 - return 주소값기반 10진수숫자;

- 오버라이딩후: book클래스의 hashcode메소드 실행 - return 멤버변수담긴값들 10진수;

 

= 항상 equals와 hashcode는 세트다. 항상 붙어다닌다. 

 

 

[내가한거]

public int hashCode() {

 

     if(title.equals("자바")&&author.equals("홍길동")&&price==300) {

          return 100;

       }

      else

          return 0;

}

 

 

[그런데 더 간단히]

 

     return (title + author + price).hashCode(); 해도 됨.

 

같은 문자열인 경우 String에서 hashCode가 같도록 이미 정의되어 있어서 이렇게 해도 됨.

 

 

- 아까 Book 클래스 equals 오버라이딩할 때,

this.title.equals(other.title) && this.author.equals(other.author) && this.price==bbb.price

대신,

this.title==other.title && this.author==other.author && this.price==bbb.price

이렇게 해도

String은 같은 문자열은 동일 문자풀에 저장하니까
"자바"랑 "홍길동"도 같은 문자풀에 저장되어서
equals말고 == 해도 결과는 같은게 맞나요?


아뇨아뇨!
리터럴 형태로 문자열을 전달했다면 ==으로 해도 true 가 나오긴 할텐데

만약에 
Book bk3 = new Book("자바", "홍길동", 300);
Book bk4 = new Book(new String("자바"), "홍길동", 300);
이런식으로 멤버변수에 값으로 
"자바" 값이 아닌 new String("자바") 이런식으로
하게 되면 그때는 == 비교시 제대로 되지 않아요!
그래서 이런경우까지 고려를 한다면 
== 보다 equals로 하는게 정확하죠!

 

- String str = new String("가나다");와 String str = "가나다";는 다른 동작을 합니다.

(1) 이 경우에는 문자열 리터럴 "가나다"가 문자열 풀(String Pool)에 저장됩니다.

(2) new String("가나다")는 새로운 객체를 생성하며, 이 때 "가나다" 문자열 리터럴이 문자열 풀에 이미 존재하더라도 새로운 객체를 생성합니다.

 

 

자바에서 메인메소드에서 어떤 메소드를 실행시켰어. 근데 그 메소드에서 return;을 만나면 전체 프로그램이 종료돼 그 어떤 메소드만 종료돼?

 
 

- 자바에서 return; 문장은 메소드의 실행을 중단하고 해당 메소드를 호출한 곳으로 반환(return)합니다.

따라서 return;을 만나면 현재 실행 중인 메소드가 종료되며, 해당 메소드를 호출한 메소드로 제어가 돌아갑니다.

전체 프로그램이 종료되는 것이 아니라 단순히 그 메소드의 실행이 종료됩니다.

 

 

ㅁ 접근범위

 

(1) 부모 타입 레퍼런스로 부모 객체를 다루는 경우

Parent p1 = new Parent();

 

p1.printParent(); 

 

p1 레퍼런스로 Parent에만 접근 가능

 

(2) 자식 타입 레퍼런스로 자식 객체를 다루는 경우

Child c1 = new Child1();

 

c1.printParent();

c1.printChild1(); // 자동형변환. 원래는 (Parent)c1.printChild1();

 

c1 레퍼런스로 Parent, Child1에 둘 다 접근 가능

 

(3) 부모 타입 레퍼런스로 자식 객체를 다루는 경우

Parent p2 = new Child1();

 

p2.printParent(); 만 가능.

 

((Child1)p2).printChild1(); 이렇게 강제형변환하면 다시 사용 가능.

// 괄호 한번 더 안씌우면 .으로 메소드가 먼저 호출되서 안됨.

 

p2 레퍼런스로 Parent에 접근 가능하나, Child1에 접근하려면 DownCasting 진행해야 함.

 

 

 

- 부모 타입으로는 모든 자식 객체를 다 담을 수 있다. 

- 자동형변환이 되고 있다. Parent p2 = (Parent) new Child1();

- 상속 구조일 때만, 클래스 간 형변환이 가능하다. 

- Casting(형변환),

UpCasting(자식 -> 부모, 자동)                 부모=자식객체;         자식.부모메소드();

DownCasting(부모 -> 자식, 강제)             ((자식)부모).자식메소드();

 

 

ㅁ 접근 가능 범위 정리

 

- Parent는 부모클래스고 변수 x, y와 메소드 method1과 method2.

- Child는 자식클래스고 변수 z와 메소드 method3과 method4.

 

(1) Parent p = new Parent();인 경우

- p는 변수 x, y 가능

- p는 메소드 method1과 method2 가능.

 

(2) Child c = new Child();인 경우

- c는 변수 x, y, z 가능

- c는 메소드 method1, method2, method3, method4 가능

 

(3) Parent xx = new Child();인 경우

- xx는 변수 x, y 가능

- xx는 메소드 method1, method2 가능

 

- (참고1) 만약 Child에서 부모 클래스의 method2를 오버라이딩 한 경우에는, 특별히 부모의 method2가 아닌 자식의 method2가 호출됨.

- 이 때 xx를 통해 호출할 수 있는 메소드는 Parent 클래스에 정의된 메소드와 Child 클래스에서 오버라이딩한 메소드들입니다.

- (참고2) 강제 형변환을 통해 Child 클래스의 메소드 호출 // z도 접근 가능. 

((Child)xx).method3();

((Child)xx).method4();

부모타입으로 선언했지만 자식객체의 메소드를 사용하고 싶으면 강제 형변환을 해야 한다.

 

※ 처음부터 Child yy = new Parent();로 선언하면?

- 불가능. new Parent()하는 순간 부모만 생성된거. 이걸 앞에 명시적으로 형변환 붙여줘도 안됨.

- 자식객체를 생성했다가, 부모 타입에 넣었다가, 다시 명시적 형변환으로 자식 타입에 넣는 것은 가능.

이건 왜 가능하냐면 처음에 new Child();를 했기 때문. 이러면 자식 + 부모를 다 가지고 있기 때문에.

 

 

 

ㅁ 부모타입 객체 배열에 자식타입 객체들을 저장하기.

- 자식 객체 2개, 또 자식 객체 2개가 있는 경우. 배열 2개에 각각 저장.

- 부모 배열 1개에 다 담을 수 있음. 이때 자동형변환이 됨.

- 그런데 부모 배열 변수[x].자식메소드();하고 싶으면 괄호 두개 씌워서 강제형변환 다시 해줘야 가능.

 

- 근데 Child2를 Parent로 했다가 Child1으로 하면, 컴파일시에는 오류(빨간줄)을 안띄우는데 실행할 때 오류를 띄움.

부적절한 형변환시 ClassCastException 발생.

 

- 그런데 몇 개 안될 경우에는 인덱스 번호에 따라 ((Child1)arr[0).printChild1();,  ((Child2)arr[1).printChild2();를 하겠지만, 엄청 많을 경우에는 이것들을 다 일일이 if로 해줄 수 없다.

대신 if( 부모타입 instanceof 비교할자식타입)으로 한다. 

 

ㅁ (이어서) instanceof 대신 오버라이딩을 활용하기.

- 부모 클래스 Parent print 메소드에 "부모" 출력.

- 자식 클래스 Child1 print 메소드에 "자식1" 출력.

- 자식 클래스 Child2 print 메소드에 "자식2" 출력.

 

- 부모객체배열 arr에 Child1, Child2 클래스가 2개씩 있다. 

for(int i=0; i<arr.length; i++){

    arr[i].print();            //        지금 arr[i]는 부모임.

}

 

// 향상된 for문으로는

for(Parent p : arr ) {

    p.print();

}

 

- 아까는 오버라이딩된 메소드가 아니라 각각의 자식 클래스가 가진 메소드를 호출해서 매번 형변환을 해줘야 했다.

오버라이딩하면 형변환 과정 필요없이 호출 가능. 

instatnceof로 비교할 필요도 없고.

 

 

※ 동적바인딩

- arr[i].print();여기서 print를 ctrl 클릭해보면 부모 메소드의 print()메소드로 감. 

- 지금 가리키는건 부모 메서드를 가리키지만 실제 실행할 때는 오버라이딩된 메소드를 참조함.

 

- 소스코드    -(컴파일)->    byte코드    -(실행)->    프로그램 실행

    .java        컴파일과정      .class      런타임과정

                                       (우리가 못봄)

 

- 이클립스에서 빨간줄 뜨는건 컴파일 에러임.

근데 컴파일 과정 중에는 문제가 없는데 실행할 때 오류가 생기는 경우가 있음. 그런걸 런타임 에러라고 함.

 

 

- 동적바인딩 : 프로그램 실행 전 "컴파일 시점"에는 정적바인딩에 의해서 부모 클래스의 메소드를 가리킴.

                       프로그램이 실행하는 순간 "런타임 시점"에는 실제로는 자식 클래스의 오버라이딩된 메소드가 실행됨.

(실질적으로 참조하고 있는 자식 클래스의 메소드를 찾아서 실행하는걸 동적바인딩이라고 함.)

 

 

ㅁ 상수필드는 선언과 동시에 값을 대입해둬야 한다.

- 상수필드에 대해서는 게터세터를 만들 필요가 없음. 

static이라 그냥도 접근 가능. 

 

 

ㅁ 부모 클래스에 추상메서드 추가할거면 그 메소드, 부모 클래스에 abstract 붙여줘야 함. 

- Sport s = new Sports(); 

이렇게 못 씀. 인스턴스화할 수 없다. 추상클래스는.

- 대신 변수 선언은 가능하다.

Sports s;

어떻게 쓰냐. 부모 타입 변수에 자식 객체 할당하면 된다. 

Sports s1 = new FootBall();

Sports s2 = new BasketBall();

 

Sports[ ] arr = new Sports[2];

arr[0] = new FootBall();

arr[1] = new BasketBall();

 

for(int i=0; i<arr.length; i++) {

    arr[i].rule();          //       부모 rule을 가리키지만 실제 실행은 오버라이딩 된 자식 메소드가 됨.

}

 

ㅁ 추상 클래스

1. 미완성된 클래스

2. 객체 생성 불가능 .단, 레퍼런스 변수로 선언은 가능.

3. 추상 메소드가 없어도 추상클래스로 선언 가능함.

- 해당 클래스를 통해 객체 생성을 불가능하게끔 하고자 할 때.

- 일반 필드 + 일반 메소드 [ + 추상 메소드]

 

※ 추상메소드를 포함한 추상클래스를 선언하는 케이스

- 자식 클래스들에 실행시킬 내용은 다르지만, 동일한 형태의 메소드로 구현됐으면 할 때

- 강제로 해당 메소드가 오버라이딩 됨. 

- 메소드의 통일성 확보 목적. 

- 표준화된 틀을 제공할 목적. 

 

 

자식 클래스에서 부모 메서드나 변수에 접근할 때,

중복되지 않는다면 super.setExp( super.getExp() + 20 ); 여기서 super. 을 생략할 수 있다.

setExp( getExp() + 20 ); 이렇게도 가능하다. 내가 가진걸 찾기 때문.

심지어 super. 대신 this.을 써도 된다. 내가 부모거를 갖고있기 때문.

 

 

ㅁ 예제

Person mom = new Mother("김엄마", 50, 70, "출산");

Person baby = new Baby("강아기", 3.5, 80);

// 다형성. 부모레퍼런스로 자식객체 받아주는거

// 하나의 타입으로 다양한 형태를 표현할 수 있기 때문에

 

System.out.println(mom);

System.out.println(baby);

// 컴파일시에는 동적바인딩으로 Person의 toString을 가리키지만, 실행시에는 오버라이딩된 자식객체의 toString을 가리킴

 

mom.eat();

baby.eat();

// 이것들도 동적바인딩.

 

 

ㅁ 인터페이스도 클래스의 일종이다

- 상수필드(static final)는 둘 수 있지만 일반 필드는 못 둔다.  (상수면 선언과 동시에 초기화해야 한다.)

- 완성형태의 메소드를 가질 수 없다.

- 즉, 오직 상수필드와 추상메소드만 가질 수 있다. 

 

- 인터페이스에서는 public static final을 생략해도 상수로 간주됨.

어차피 상수만 쓸 수 있기 때문에. 

- 인터페이스에서는 public abstract를 생략해도 추상메소드로 간주됨.

어차피 추상메소드만 쓸 수 있기 때문에.

 

 

ㅁ 

- 클래스에서 클래스를 상속받을 때 : 클래스 extends 클래스 (단일상속) [실선]

- 클래스에서 인터페이스를 구현할 때 : 클래스 implements 인터페이스, 인터페이스 (다중상속) [점선]

- 인터페이스에서 인터페이스를 상속받을 때 : 인터페이스 extends 인터페이스, 인터페이스 (다중상속) [실선]

 

ㅁ 인터페이스를 구현하는 추상 클래스는 인터페이스의 모든 추상 메서드를 반드시 구현할 필요는 없습니다.

'클라우드 활용 자바개발자 양성과정 > 01. JAVA 프로그래밍 언어 활용' 카테고리의 다른 글

11. 예외처리  (0) 2024.07.09
10. API  (0) 2024.07.05
07. 객체 배열  (0) 2024.07.02
06. 객체  (0) 2024.06.27
05. 배열  (0) 2024.06.24