🟨 목 차 🟨
1. 자바에서 상속이란 ?
1-1. 상속 정리
2. 상속과 생성자
3. 메소드 overriding
3-1. 메소드 overriding 정리
4. 참조 자료형의 형 변환
5. Polymorphism (다형성)
6. 정리
◼️ 1. 자바에서 상속이란 ? ◼️
자바에서 상속이란 말보다는 예시를 보며 이해하는게 빠르기 때문에 예시를 살펴보자.
[EX]
package part10;
public class Parent {
public Parent() {
System.out.println("Parent Constructor");
}
public void pintName() {
System.out.println("Parent printName()");
}
}
package part10;
public class Child extends Parent {
public Child() {
System.out.println("Child Constructor");
}
}
위해서 public class Child extends Parent 부분을 보자. extends라는 것은 자바의 예약어이며, 그 다음에 클래스 이름을 지정하면 그 클래스를 상속받는다는 말이다.
이렇게 상속을 받으면 부모 클래스에 선언되어 있는 public 및 protected로 선언되어 있는 모든 변수와 메소드를 내가 갖고 있는 것처럼 사용할 수 있다.
즉, 다른 패키지에 선언된 부모 클래스의 접근제어자가 없거나(package-private) private로 선언된 것들은 자식 클래스에서 사용할 수 없다.
이 예에서는 Parent 클래스에 printName()이라는 public한 메소드가 있기 때문에, Child 클래스에서 printName() 이라는 메소드를 마음껏 사용할 수 있다.
자식클래스에서 객체를 생성해 부모 클래스에 있는 메소드를 사용해보자.
[EX]
package part10;
public class InheritancePrint {
public static void main(String[] args) {
Child child = new Child();
child.pintName();
}
}
Parent Constructor
Child Constructor
Parent printName()
◼️ 1-1. 상속 정리
즉, 상속에 대해 정리를 해보면
- 부모클래스에서는 기본 생성자를 만들어 놓는 것 이외에는 상속을 위해서 아무런 작업을 할 필요가 없다.
- 자식 클래스는 클래스 선언시 extends 다음에 부모 클래스 이름을 적어준다.
- 자식 클래스의 생성자가 호출되면, 자동으로 부모 클래스의 매개 변수 없는 생성자가 실행된다.
- 자식 클래스에서는 부모 클래스에 있는 public, protected로 선언된 모든 인스턴스 및 클래스 변수와 메소드를 사용할 수 있다.
왜 상속이라는 개념을 왜 만들었을까 ?
==> 부모 클래스가 갖고 있는 변수와 메소드를 상속받음으로써, 개발을 할 때 이중, 삼중의 일을 안해도 되기 때문이다.
상속이라는 개념을 실제 세계로 예시를 들어보면, 맥북에는 많은 시리즈의 맥북이 있다. 각 모델을 처읍부터 따로 디자인한다고 하면 엄청난 시간과 인건비가 소요될 것이다. 하지만, 하나를 제대로 만들어 놓고,그것에서 파생되는 것들을 조금씩 바꾸어 판매하면 훨씬 효율적일 것이다.
즉, 자바로 말을 하게 된다면 하나의 클래스를 잘 만들어 놓은게 있다면, 그 클래스를 상속받아 내가 추가적인 기능을 넣을 수 있는것이다.
모든 클래스에 printName()라는 메소드를 만들어야 하는 상황이다. 여기서 상속이라는 개념이 없다면 printName()라는 메소드를 모든 클래스에 만들어야 한다. 하지만 상속을 이용한다면 편하다. 또한
printName()이라는 메소드에 문제가 있어 고쳐야 하는 상황이 발생한다면 모든 클래스에 있는 printName() 메소드를 고쳐야한다.
==> 하지만 상속을 이용하면 부모 클래스에 있는 메소드를 고치면 자식 클래스의 메소드도 자동으로 적용되기 때문에 매우 큰 장점을 가지고 있다.
상속받은 클래스는 추가적인 메소드를 만들어도 전혀 문제가 없다.
◼️ 2. 상속과 생성자 ◼️
상속에 대해 부모 클래스에서는 기본 생성자를 만들어 놓은 것 이외에는 상속을 위해서 아무런 작업을 할 필요는 없다고 했다.
만약 부모 클래스에 기본 생성자가 없다면 어떻게 될까? 예시를 보며 결과를 확인하자.
package part10;
public class Parent {
// public Parent() {
// System.out.println("Parent Constructor");
// }
public void pintName() {
System.out.println("Parent printName()");
}
}
package part10;
public class InheritancePrint {
public static void main(String[] args) {
Child child = new Child();
child.pintName();
}
}
Child Constructor
Parent printName()
부모 클래스(Parent)의 생성자를 주석 처리를 하고 실행시키면 정상적으로 위와 같은 결과 값이 뜨게 된다.
즉, 부모 클래스의 생성자가 없어도 정상적으로 처리된다.
하지만 부모 클래스에 생성자 부분에 매개 변수를 받는 메소드가 있을 경우는 달라진다.
[ex]
package part10;
public class ParentArg {
public ParentArg(String name) {
System.out.println("ParentArt("+name+") Constructor");
}
public void printName() {
System.out.println("printName() - ParentArg" );
}
}
package part10;
public class ChildArg extends ParentArg{
public ChildArg() {
System.out.println("Child Constructor");
}
}
위의 코드에서 ChildArg 클래스를 보게되면 에러가 뜨게 된다.
==> 부모클래스(ParentArg)의 상속을 받은 ChildArg 클래스의 모든 생성자가 실행될 때 ParetArg()라는 기본 생성자를 찾는데, ParentArg클래스에 정의한 생성자는 String을 매개 변수로 받는 생성자밖에 없기 때문이다. [ 매개변수가 있는 생성자를 만들었을 경우에는 기본 생성자는 자동으로 만들어지지 않는다. ]
위의 문제를 해결 할 수 있는 방법은 2가지이다.
- 부모 클래스에 "매개 변수가 없는" 기본 생성자를 만든다.
- 자식 클래스에서 부모 클래스의 생성자를 명시적으로 지정하는 super()를 사용한다.
여기서 super() 예약어를 사용하게되면 부모클래스의 생성자를 호출한다는 의미이다.
위의 문제 있는 코드를 이렇게 고치면 실행이 된다.
[EX]
package part10;
public class ChildArg extends ParentArg{
public ChildArg() {
super("ChildArg");
System.out.println("Child Constructor");
}
}
ParentArt(ChildArg) Constructor
Child Constructor
위의 코드를 보게 되면 super("ChildArg")라고 지정하면, ParentArg 클래스의 생성자 중 String 타입을 매개변수로 받을 수 있는 생성자를 찾는다. String을 매개변수로 받는 생성자가 있기 때문에, 이 생성자가 호출된다.
그런데, 이 생성자 처럼 참조 자료형을 매개 변수로 받는 생성자가 하나 더 있다면 어떻게 될까?
package part10;
public class ParentArg {
public ParentArg(String name) {
System.out.println("ParentArt("+name+") Constructor");
}
public ParentArg(InheritancePrint object) {
System.out.println("ParentArg(InheritancePrint) Constructor");
}
public void printName() {
System.out.println("printName() - ParentArg" );
}
}
package part10;
public class ChildArg extends ParentArg{
// ChildArg exam = new ChildArg();
public ChildArg() {
// super("ChildArg");
super(null);
System.out.println("Child Constructor");
}
}
결과는 컴파일 오류가 난다. 오류 내용으로는 참조가 모호하다라고 뜬다.(부모 클래스에서 매개변수가 있는 생성자가 2개라서 super()가 무엇을 참조할 지 모른다는 의미로 모호하다고 한다.)
==> 따라서,supe()를 사용하여 생성자를 호출할 때에는 모하하게 null을 넘기는 것보다는 호출하고자 하는 생성자의 기본 타입을 넘겨주는것이 좋다. 여기서는 ParentArg 클래스에 String을 매개변수로 받는 생성자와 InheritancePrint 클래스를 매개 변수로 받는 클래스가 있으므로, null을 넘겨주면 어떤 클래스를 찾아가야 하는지 컴파일러가 모른다.
중요한 건 부모 클래스에 매개변수가 있는 생성자만 있을 경우에는 super()를 이용해서 부모 생성자를 꼭 호출해야만 한다.
◼️ 3. 메소드 overriding ◼️
상속의 관계에서 기능면에서 살펴보면 이렇게 생각하면 편하다.
부모 클래스에서 private, package-private 선언된 메소드나 변수 빼고는 사용이 가능하다.
자식 클래스에서 부모 클래스에 있는 메소드와 동일하게 선언하는 것은 "메소드 Overriding"이라고 한다.
==> 접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수가 모두 동일해야만 "메소드 Overriding"이라고 부른다.
[ Overriding이라는 단어의 사전적 의미는 "다른 무엇보다 더 중요한, 최우선시되는" 이라는 뜻이다.
예제를 통해 알아보자.
[EX]
package part10;
public class ParentOverriding {
public ParentOverriding() {
System.out.println("ParentOverriding Constructor");
}
public void printName() {
System.out.println("printName() - ParentOverriding");
}
}
package part10;
public class ChildOverriding extends ParentOverriding {
public ChildOverriding() {
System.out.println("ChildOverriding Constructor");
}
public void printName() {
System.out.println("ChildOverriding printName()");
}
}
package part10;
public class InheritanceOverriding {
public static void main(String[] args) {
ChildOverriding child = new ChildOverriding();
child.printName();
}
}
ParentOverriding Constructor
ChildOverriding Constructor
ChildOverriding printName()
위 코드를 보게되면 InheritanceOverriding클래스에서 자식클래스인 ChildOverriding 객체를 생성하고 printName()메소드를 출력한다. 부모클래스에 있는 printName()의 출력문이 출력이 되는게 아니라 자식 메소드의 printName()이라는 것이 출력된다. 이것을 "메소드 Overriding"라고한다.
Overriding과 생성자와 비교를 한다면,
- Overriding은 부모클래스와 자식클래스의 접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수가 모두 동일한 메소드 가 있다고 치면 자식클래스에서 이 메소드를 선언하게 되면 Overriding이라고 한다.
- 생성자는 자동으로 부모 클래스에 있는 생성자를 호출하는 super()가 추가된다.
부모 클래스에 자식클래스의 접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수가 모두 동일한 메소드가 있다고 치자.
자식 클래스에서 이 메소드를 바꾸는데(Overriding)을 하는데 안에 있는 내용을 바꾸는게 아니라 리턴 타입을 바꾸면 어떻게 될까?
==> 에러가 발생한다. 예시로 보자.
[EX]
package part10;
public class ChildOverriding extends ParentOverriding {
public ChildOverriding() {
System.out.println("ChildOverriding Constructor");
}
public String printName() {
System.out.println("ChildOverriding printName()");
}
}
부모 클래스에 자식클래스의 접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수가 모두 동일한 메소드가 있다고 치자.
자식 클래스에서 이 메소드를 바꾸는데(Overriding)을 하는데 안에 있는 내용을 바꾸는게 아니라 접근 제어자를 바꾸면 어떻게 될까?
==> 예시를 들어 결과를 알아보자^^
예를 들어 부모클래스의 메소드의 접근제어자는 public이고 자식 클래스가 Override를 하여 접근제어자를 private로 바꾼다.
==> 결과는 컴파일 오류로 뜬다. 그 이유는 자식 클래스는 부모 클래스보다 더 약한 접근 권한을 지정을 했기 때문이다.
즉, public > protected > package-private > private (오른쪽으로 갈수록 접근권한이 약해진다.) 부모가 만약 private로 선언했으면 자식은 접근 제어자를 어떤 것을 선언하든지 상관없다. 왜냐하면 private 보다 접근 권한이 약한것은 없기 때문이다.
◼️ 3-1. 메소드 overriding 정리
[정리]
- 메소드 Overriding은 부모 클래스의 메소드와 동일한 시그니처를 갖는 자식 클래스의 메소드가 존재할 때 성립된다.
- Overriding된 메소드는 부모 클래스와 동일한 리턴 타입을 가져야만 한다.
- Overriding된 메소드의 접근 제어자는 부모 클래스에 있는 메소드와 달라도 되지만, 접근 권한이 확장되는 경우에만 허용된다.
접근 권한이 축소될 경우에는 컴파일 에러가 발생한다.
Overloading과 Overriding과 차이
- Overloading : 확장 (메소드의 매개 변수들을 확장하기 때문에, 확장)
- Overriding : 덮어 씀(부모 클래스의 메소드 시그니처를 복제해서 자식 클래스에서 새로운 것을 만들어 내어 부모 클래스의 기능은 무시하고, 자식 클래스에서 덮어씀)
◼️ 4. 참조 자료형의 형 변환 ◼️
package part10;
public class ParentCasting {
public ParentCasting() {
}
public ParentCasting(String name) {
}
public void printname() {
System.out.println("printName() - Parent");
}
}
package part10;
public class ChildCasting extends ParentCasting {
public ChildCasting() {
}
public ChildCasting(String name) {
}
public void printName() {
System.out.println("printName() - Child");
}
public void printAge() {
System.out.println("printAge() - 18 month");
}
}
위는 부모클래스인 ParentCating 클래스와 자식 클래스인 ChildCasting 클래스가 있다.
기본적으로 객체를 생성할 때는, 아래처럼 생성한다.
ParentCasting parent = new ParentCasting();
ChildCasting child = new ChildCasting();
그런데 상속 관계가 성립되면 지금까지 객체를 생성한 것과는 다르게, 아래와 같이 객체를 생성할 수도 있다.
ParentCasting obj = new ChildCasting();
하지만 아래와 같이는 객체를 생성 할 수는 없다.
ChildCasting obj2 = new ParentCasting();
왜 어떻게 하면 되고 어떻게 하면 안되는 것일까?
==> 자식 클래스인 ChildCasting클래스에서는 부모 클래스인 ParentCasting 클래스에 있는 메소드와 변수들을 사용할 수 있다. 그런데 거꾸로 부모 클래스인 ParentCasting 클래스에서는 ChildCasting 클래스에 있는 모든 메소드와 변수들을 사용할 수 없기 때문이다.
부모클래스에서는 자식클래스에 있는 모든 메소드와 변수를 사용할 수도 있고, 그렇지 않을 수도 있다. 모든 메소드와 변수를 사용할 수 있을때는 자식 클래스에 추가된 메소드나 변수가 없으면 가능할 것이다. 하지만 자바 컴파일러에서는 자식 객체를 생성할때 부모 생성자를 사용하면 안된다고 못을 박아 버린다.
==> 명시적으로 형 변환을 한다고 알려줘야만 한다.
[ 기본 자료형 형변환인 경우에 int->long은 별도의 형변환이 필요없지만, long->int 로 변환을 하려면 값이 바뀔 확률이 있기 때문에 개발자가 "내가 이건 책임질께"라고 명시적으로 형 변화을 해야 한다. ]
[ex]
int intValue=10;
long longValue=101;
long casted1=intValue;
int cated2=(int)longValue;
==> 동일한 값이 된다는 보장이 전혀 없다.(여기서는 10이므로 우리가 보기엔 동일하게 느껴지겠지만, longValue가 int의 범위를 넘어서면 이 값은 바뀐다.)
참조자료형의 경우 자식클래스의 타입을 부모 클래스의 타입으로 형 변환하면 부모 클래스에서 호출할 수 있는 메소드들은 자식 클래스에서도 호출할 수 있으므로 전혀 문제가 안된다. 따라서 형 변환을 우리가 명시적으로 해 줄 필요가 없다. 예시를 보자.
[EX]
package part10;
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= parent;
}
}
위 코드를 보게 되면 ChildCasting child2 = parent 이 부분에서 컴파일 오류가 뜬다.
즉, 자식 클래스 타입에서 부모 클래스 타입으로 형 변환을 하면 문제가 되지 않으나, 부모 클래스 타입에서 자식 클래스 타입으로 형 변화을 하면 문제가 되는것이다. 이유는 parent 객체는 ParentCasiting 클래스 객체이며, ChildCasting 클래스에 선언되어 있는 메소드나 변수를 완전히 사용할 수 없기 때문이다. 컴파일 오류를 피하기 위해서는 아래와 같이 한다.
==> ChildCasting child2 = (ChildCasting) parent;
위의 의미는 "parent 객체 너는 ChildCasting 클래스 인 것 처럼 행동해" 라고 명시적으로 선언해주는 것이다.
위와 같이 명시적으로 선언을 해주고 실행을 하면 컴파일 오류가 뜬다.
==> 왜냐하면 parent 객체는 실제로 ParentCasting 클래스의 객체이므로 컴파일 오류는 넘겼지만, 실행시에는 "얘는 원래 ParentCasting 클래스의 객체라서 못쓰겠어요"라면서 예외가 발생한 것이다.
그렇다면 언제 이런 명시적인 형 변화을 해도 문제가 없는 것일까? 예시를 보면 알아보자.
[EX]
public void objectCast2() {
ChildCasting child = new ChildCasting();
ParentCasting parent2 = child;
ChildCasting child2=(ChildCasting)parent2;
}
==> ParentCating parent2 = child; 을 보게 되면 parnt2는 child를 대입한 것이다. 그리고 child는 ChildCasting 클래스의 객체이다.
Parent2로 겉모습은 ParentCasting 클래스의 객체인 것처럼 보이지만, 실제로는 ChildCating 클래스의 객체이기 때문에(parnt2는 child를 대입한 것이여서) parent2를 ChildCasting 클래스로 형 변환해도 문제가 없는것이다.
위의 방법은 자식 클래스를 부모클래스로 형 변환을 한 객체를 다시 자식 클래스로 형변환을 하여 머리가 아플 수 있다. 예시를 보며 해결해보자.
[EX]
public void objectCastArray() {
ParentCasting[] parentArray = new ParentCasting[3];
parentArray[0] =new ChildCasting();
parentArray[1] =new ParentCasting();
parentArray[2] =new ChildCasting();
}
==> 위의 코드를 보면 ParentCasting 배열은 3개의 값을 저장할 수 있는 값을 가진다. 그런데 0번째 배열과 2번째 배열은 ChildCasting 클래스의 객체를 할당한 것을 볼 수 있다. 이렇게 코딩해도 전혀 문제가 없다.
하지만 이와같이 일반적으로 여러 개의 값을 처리하거나, 매개 변수로 값을 전달할 때에는 보통 부모 클래스의 타입으로 보낸다.
그렇게 하지 않으면 배열과 같이 여러 값을 한번에 보낼 때 각 타입별로 구분해서 메소드를 만들어야 하는 문제가 생길 수도 있기 때문이다.
==> partArray라는 배열의 타입이 ChildCasting인지 ParentCasting인 지 어떻게 구분할 수 있을까? 바로 instanceof 예약어를 사용하면 된다. 예시를 보면 확인하자.
[EX]
public void objectCastArray() {
ParentCasting[] parentArray = new ParentCasting[3];
parentArray[0] =new ChildCasting();
parentArray[1] =new ParentCasting();
parentArray[2] =new ChildCasting();
objectTypeCheck(parentArray);
}
private void objectTypeCheck(ParentCasting[] parentArray) {
int i = 0;
for(ParentCasting tempParent:parentArray) { //parentArray의 값을 순서대로 tempParent에 차례대로 대입한다라는 뜻
if(tempParent instanceof ChildCasting) {
System.out.println("ChildCasting");
System.out.println(i);
i++;
}
else if(tempParent instanceof ParentCasting) {
System.out.println("ParentCasting");
System.out.println(i);
i++;
}
}
}
ChildCasting
ParentCasting
ChildCasting
==> instanceof의 앞에는 객체를, 뒤에는 클래스 타입을 지정해주면 된다. 이 문장은 true,false 와 같이 boolean 형태의 결과를 제공한다. 결과값으로 parentArray의 0번째 값은 ChildCasting 클래스의 객체이므로 if문 true(ChildCasting 출력), 1번째 값은 else if문 ture(ParentCasting 출력), 2번째 값은 if문 true(ChildCasting 출력)의 결과를 알 수 있다.
이와 같이 instanceof를 통해 타입을 확인 한 후에 명시적으로 형 변환을 하면 전혀 문제가 없다.
예시를 들어 설명하겠다.(ChildCasting 클래스에서만 선언되어 있는 printAge()메소드를 출력)
[EX]
private void objectTypeCheck(ParentCasting[] parentArray) {
int i = 0;
for(ParentCasting tempParent:parentArray) { //parentArray의 값을 순서대로 tempParent에 차례대로 대입한다라는 뜻
if(tempParent instanceof ChildCasting) {
System.out.println("ChildCasting");
ChildCasting tempChild = (ChildCasting) tempParent;
tempChild.printAge();
System.out.println(i);
i++;
}
else if(tempParent instanceof ParentCasting) {
System.out.println("ParentCasting");
System.out.println(i);
i++;
}
}
}
==> 만약 타입이 ParentCasting인 객체는 printAge() 메소드가 없으므로, 무작정 형 변환하여 printAge() 메소드를 호출하면 분명 예외가 생실것이다. 이렇게 instanceof를 사용하면 정확하게 타입을 확인할 수 있고, 원하는 메소드도 호출할 수 있다.
그런데, instanceof를 사용하면서 유의할 점이 하나 있다. parentArray의 0번째 값은 분명히 ChildCasting 타입이다. 그런데 ParentCasting 타입이 될까? 즉, System.out.println(parentArray[0] instanceof ParentCasting); >> 이 결과는 false로 찍힌다면, ChildCasting 클래스의 객체는 parentArray에 넣지도 못한다.( parentArray[0],[2]는 ChildCasting타입도 되고 ParentCasting타입도 된다는 말이다.)
==> parentArray[0],[2]는 ChildCasting타입도 되고 ParentCasting타입도 되기 때문에 instanceof로 타입을 점검할 때 ParentCating의 인스턴인지 여부를 먼저 점검하면 안된다. 즉,ChildCasting 타입 검사를 먼저하고 그 다음 ParentCating검사를 해야한다는 의미이다. 이것을 말로 표현하면,
<ParentCasting 타입을 먼저 검사하고 ChildCasting을 타입을 검사한 예 >> 잘못된 예>
private void objectTypeCheck(ParentCasting[] parentArray) {
int i = 0;
for(ParentCasting tempParent:parentArray) { //parentArray의 값을 순서대로 tempParent에 차례대로 대입한다라는 뜻
if(tempParent instanceof ParentCasting) {
System.out.println("ParentCasting");
System.out.println(i);
i++;
}
else if(tempParent instanceof ChildCasting) {
System.out.println("ChildCasting");
ChildCasting tempChild = (ChildCasting) tempParent;
tempChild.printAge();
System.out.println(i);
i++;
}
}
}
ParentCasting
0
ParentCasting
1
ParentCasting
2
<ChildCasting 타입을 먼저 검사하고 ParentCasting을 타입을 검사한 예 >> 잘된 예>
private void objectTypeCheck(ParentCasting[] parentArray) {
int i = 0;
for(ParentCasting tempParent:parentArray) { //parentArray의 값을 순서대로 tempParent에 차례대로 대입한다라는 뜻
if(tempParent instanceof ChildCasting) {
System.out.println("ChildCasting");
ChildCasting tempChild = (ChildCasting) tempParent;
tempChild.printAge();
System.out.println(i);
i++;
}
else if(tempParent instanceof ParentCasting) {
System.out.println("ParentCasting");
System.out.println(i);
i++;
}
}
}
0
ChildCasting
printAge() - 18 month
1
ParentCasting
2
ChildCasting
printAge() - 18 month
◼️ 5. Polymorphism (다형성) ◼️
다형성이란 "형 변환을 하더라도, 실제 호출되는 것은 원래 객체에 있는 메소드가 호출된다"는 것이 다형성이다.
Overriding과 형변환을 이해했으면 쉽게 이해할 수 있을 것이다. 예시를 보자.
[EX]
package part10;
public class ChildOther extends Parent{
public ChildOther() {
}
public void printName() {
System.out.println("ChildOther - printName()");
}
}
==> 위의 코드느 Child 클래스를 복사한 후 printName() 메소드의 내용과 클래스 선언부, 생성자 선언부만 변경한 것이다. 이렇게 만들면 Parent 클래스의 자식 클래스가 두 개가 된 것이다. 자바에서는 자식 클래스는 백개를 만들든, 만개를 만들든 전혀 문제 없다.
[EX]
package part10;
public class InheritancePoly {
public static void main(String[] args) {
InheritancePoly inheritance = new InheritancePoly();
inheritance.callPrintNames();
}
public void callPrintNames() {
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이다.
다형성을 예제로 표현을 한다면,
Parent pa = new Parent(); // 허용
Child ch = new Child(); // 허용
Parent pc = new Child(); // 허용
Child cp = new Parent(); // 오류 발생.
◼️ 6. 정리 ◼️
- 생성자 -
- 자식 클래스의 생성자가 호출되면 자동으로 부모 클래스의 매개 변수가 없는 기본 생성자가 호출된다. 명시적으로 super()라고 지정할 수 도 있다.
- 부모 클래스의 생성자를 명시적으로 호출하려면 super()를 사용하면 된다.
- 변수 -
- 부모 클래스에 private로 선언된 변수를 제외한 모든 변수가 자신의 클래스에 선언된 것처럼 사용 할 수 있다.
- 부모 클래스에 선언된 변수와 동일한 이름을 가지는 변수를 선언할 수도 있다. 하지만, 이렇게 엎어 쓰는 것은 권장하지 않는다.
- 부모 클래스에 선언되어 있지 않는 이름의 변수를 선언할 수 있다.
-메소드-
- 변수처럼 부모 클래스에 선언된 메소드들이 자신의 클래스에 선언된 것처럼 사용할 수 있다.
- 부모 클래스에 선언된 메소드와 동일한 시그니처를 사용함으로써 메소드를 overriding 할 수 있다.
- 부모 클래스에 선언되어 있지 않은 이름의 새로운 메소드를 선언할 수 있다.
'JAVA > 자바의신 1' 카테고리의 다른 글
12장 모든 클래스의 부모 클래스는 Object에요 (1) | 2022.09.08 |
---|---|
11장 매번 만들기 귀찮은데 누가 만들어 놓은 거 쓸 수 없나요? (0) | 2022.09.08 |
9장 자바를 배우면 패키지와 접근 제어자는 꼭 알아야 해요 (0) | 2022.09.07 |
8장 참조 자료형에 대해서 더 자세히 알아보자. (0) | 2022.09.06 |
7장 여러 데이터를 하나에 넣을 수는 없을까?(배열) (0) | 2022.09.03 |