자바에서 상속이란?
ex. Parent & Child 두 개의 클래스 / c.inheritance라는 패키지 사용 c폴더 아래 inheritance라는 폴더 만든 후 Parent클래스의 소스가 위치해야함
package c.inheritance; ㅤ public class Parent { public Parent() { System.out.println(“Parent Constructor”); public void printName() { System.out.println(“Parent printName()”); } }
parent클래스에는 생성자와 printName()이라는 메소드가 있다. 지금까지 봐왔던 클래스들과 다른게 없음
package c.inheritance; ㅤ public class Child extends Parent { public child() { System.out.println(“Child Constructor”); } }
Child클래스에는 그냥 생성자만 있음 /
“Child extends Parent”
= child는 parent클래스를 상속받는다
->부모 클래스에 선언되어있는 public 및 protected로 선언되어있는 모든 변수와 메소드를 내가 갖고있는 것처럼 사용할 수 있다.
UML(unified modeling language)로 봤을 때 child가 parent를 가리키고있음
UML이란 class diagram, sequence diagram, use-case diagram 등 프로그램 만들기 전 설계
package c.inheritance; ㅤ public class INheritancePrint { public static void main(String[] args) { Child child = new Child(); child.printName(); } }
출력
Parent Constructor Child Constructor Parent printName()
(Parent클래스의 메소드를 호출하지 않아도 확장한 클래스가 생성자를 호출하면 자동으로 부모 클래스의 기본생성자가 호출됨
-> 그래서 Parent Constructor라는 문장이 출력)
정리
*상속 사용하는 이유: 하나를 제대로 만들어 놓은게 있으면 그 클래스를 상속받아 내가 추가적인 기능을 넣을 수 있음 & 각기 다른 클래스/메소드를 만들어 놨다면 수정할 때 한 번에 다 고쳐야함
*내가 이해한 것 : Child에 있는 메소드 호출 -> 생성자 메소드 호출 -> Child 메소드 실행 -> 생성자에 있지만 Child에 없는 메소드 호출
상속과 생성자
부모클래스에서는 기본 생성자를 만들어 놓는 것 이외에는 상속을 위해서 아무런 작업할 필요 없음
but, 만약 부모클래스에 기본 생성자가 없다면 ?
ChildArg() 클래스의 생성자가 실행할 때 ParentArg 클래스의 매개변수가 없는 기본 생성자를 찾는데 예시의 생성자는 String을 매개변수로 받는 생성자밖에 없기 때문
-> 그래서 부모 클래스에 “매개 변수가 없는” 기본 생성자를 만든다
OR
자식 클래스에서 부모 클래스의 생성자를 명시적으로 지정하는 super()사용
메소드 Overriding
부모 클래스에 선언되어있는 메소드와 동일한 메소드를 자식 클래스에서 선언해서 사용할 수 있게 하는 것
1) 접근 제어자
2) 리턴 타입
3) 메소드이름
4) 매개변수 타입 및 개수가 모두 동일해야 메소드 overriding이라고 부름
parent & child 클래스에 동일한 이름/매개변수타입/리턴타입/접근제어자의 메소드가 있는경우,
Parent 클래스에 선언된 메소드가 아니라 Child 클래스에 선언된 메소드가 출력 (나머지 부모 클래스 메소드는 그대로 시행됨)
-> 생성자의 경우 자동으로 부모클래스에 있는 생성자를 호출하는 super()가 추가되지만 메소드는 그렇지 않음 = 메소드 overriding
동일하게 선언되어있다 = 동일한 시그니처 (signature)를 가진다.
overriding이 우선하기 때문에 리턴타입 / 접근제어자가 다르면 에러가 남
하지만 부모 – private / 자식 – public의 경우 가능하다
즉 자식 메소드의 접근제어자가 더 넓은 범위어야한다
( public > protected > package-private > private)
메소드 overloading : 매개변수 확장
메소드 overriding : 덮어씀 (부모 클래스의 시그니처 복제해서 자식 클래스에서 새로운 것을 만들어내어 부모 클래스의 기능 무시하고 자식클래스에 덮어씀)
참조 자료형의 형 변환
객체 생성 시
ParentCasting parent = new ParentCasting();
ChildCasting child = new ChildCasting();
ParentCasting obj = new ChildCasting(); (O)
ChildCasting obj2 = new ParentCasting(); (X)
-> 확장 VS 축소
(전자)자식 클래스 -> 부모 클래스 타입 형 변환 = 부모 클래스에서 호출할 수 있는 메소드들은 자식 클래스에서도 호출할 수 있으므로 문제 없음
(후자)부모 클래스 -> 자식 클래스 타입 형 변환 = 자식 클래스에서 호출할 수 있는 메소드들은 부모 클래스에서 호출 불가
package c.inheritance; ㅤ public class InheritanceCasting { public static void main(String[] args) { InheritanceCasting inheritance = new InheritanceCasting(); Inheritance.objectCast(); } public void objectCast() { ParentCasting parent = new ParentCasting(); ChildCasting child = new ChildCasting(); ㅤ ParentCasting parent2 = child; ChildCasting child2 = (ChildCasting)parent; } }
but 에러 : parent객체는 실제로 ParentCasting클래스의 객체이므로 컴파일 오류는 넘겼지만 “원래 ParentCasting 클래스의 객체라서 못써”라는 예외 발생
-> 그렇다면 언제 이런 명시적인 형 변환해도 문제 없는 것?
+추가
public void objectCast2() { ChildCasting child = new ChildCasting(); ParentCasting parent2 = child; // 위 둘 합쳐서 ParentCasting parent2 = new ChildCasting(); ChildCasting child2 = (ChildCasting)parent2 }
parent2는 child를 대입한 것 / child는 ChildCasting 클래스의 객체 / parent2의 겉모습은 ParentCasting 클래스 객체처럼 보이지만 실제로는 ChildCasting 클래스의 객체이기 때문에 parent2를 ChildCasting으로 형변환해도 문제 없음
instanceof : 타입 구분
class Parent{} class Child extends Parent{} public class InstanceofTest{ public static void main(String[] args) { Parent parent = new Parent(); Child child = new Child(); System.out.println( parent instanceof Parent ); // true System.out.println( child instanceof Parent ); // true System.out.println( parent instanceof Child ); // false System.out.println( child instanceof Child ); // true } }
1) parent instanceof Parent : true
2) child instanceof Parent : 상속받았기 때문에 true
3) parent instanceof Child : 역상속 관계이기 때문에 false
4) child instanceof Child : true
polymorphism (폴리몰피즘)
=다형성
package c.inheritance; ㅤ public class Child extends Parent{ public ChildOther() { } public void printName() { System.out.println(“Child – printName()”); } }
package c.inheritance; ㅤ public class ChildOther extends Parent{ public ChildOther() { } public void printName() { System.out.println(“ChildOther – printName()”); } }
public class Parent { public Parent() { System.out.println(“Parent Constructor”); public void printName() { System.out.println(“Parent printName()”); } }
package c.inheritance; ㅤ public class InheritancePoly { public static void main(String args[]) { InheritancePoly inheritance = new INheritancePoly(); inheritance.callPrintNames(); } public void callPrintNamesI() { Parent parent1 = new Parent(); Parent parent2 = new Child(); Parent parent3 = new ChildOther(); ㅤ parent1.printName(); parent2.printName(); parent3.printName(); } }
출력
parent constructor parent constructor child constructor parent constructor childother constructor parent printName() parent printName() ChildOther printName()
-> 각 객체 타입은 모두 Parent타입으로 선언되어있는데도 불구하고 printName() 메소드의 결과는 상이하다. 즉 선언시에는 모두 parent타입이지만 실제로 호출된 메소드는 생성자를 사용한 클래스에 있는 것이 호출됨 -> 왜냐하면 각 객체의 실제 타입이 다르기 때문
= 형변환을 하더라도 실제 호출되는 것은 원래 객체에 있는 메소드가 호출된다 = polymorphism