basic/java 실습

day 13 예제 및 문제- 상속과 생성자, 메소드 오버라이딩

못지(Motji) 2021. 5. 10. 23:42

📖 서브와 수퍼클래스의 생성자 호출과 실행 예제

package day13;

class A{ // 부모클래스A > 설명하기 쉽게 말하면 조부모클래스라 볼 수 있다.
	A(){
		System.out.println("생성자 A");
	}
}

class B extends A{ // 부모클래스A를 상속받은 자식클래스B
	B(){
		System.out.println("생성자 B");
	}
}

class C extends B{ // 부모클래스B(A를 상속받음)를 상속받은 자식클래스C
	C(){
		System.out.println("생성자 C");
	}
}

public class InheritEx03 {
	public static void main(String[] args) {
		C c = new C();
		
	}
}

🖥️ console 출력 화면

🔈 상속받은 상태에서 서브클래스가 생성될때, 서브&수퍼클래스 생성자가 모두 실행되는데 순서는 수퍼 > 서브이다. class C는 B에게 상속 받았지만 B는 A에게 상속 받았기에 제일 위에있는 A > B > C 순서로 생성자가 실행된다. 그리고 생성시 지정하지 않았기에 기본 생성자가 실행되는데 우선 위 예제 코드에서는 기본생성자밖에 없다.

📖 수퍼 클래스의 기본생성자가 자동으로 선택되는 경우

package day13;

class A1{ // 부모클래스
	
	A1(){ // A1 기본생성자
		System.out.println("생성자 A");
	}
	
	A1(int x){ // A1의 매개변수 int를 받는 생성자
		System.out.println("생성자 A-param");
	}
}
class B1 extends A1{ // 자식클래스
	B1(){ // B1 기본생성자
		System.out.println("생성자 B");
	}
	B1(int x){ // B1의 매개변수 int를 받응 생성자
		System.out.println("생성자 B-param");
	}
}
public class InheritEx04 {
	public static void main(String[] args) {
		B1 b = new B1(5); // 매개변수 5를 던져서 객체 생성
	}
}

🖥️ console 출력화면

🔈 class B1 매개변수를 받는 생성자를 실행하여 객체 생성할때 B1은 매개변수를 받는 생성자를 실행하지만 부모클래스인 A1은 특별한 수퍼생성자 지정이 없기에 기본생성자를 실행한다. 실행순서는 부모 > 자식임으로 부모클래스 ①A1의 기본생성자 실행 ②B1의 매개변수를 받는 생성자가 실행된다. 이때, A1의 기본생성자가 없으면 Error!

📖 서브클래스에서 수퍼클래스 생성자 선택되는 경우 → super() 이용

package day13;

class A2{
	A2(){ // A2 기본생성자
		System.out.println("생성자 A");
	}
	A2(int x){ // A2 매개변수를 받는 생성자
		System.out.println("생성자 A_param :" + x);
	}
}
class B2 extends A2{
	B2(){ // B2 기본생성자
		//super(); // 명시적으로 작성하면 좋지만 생략하여도 컴파일러가 부모의 기본생성자를 호출
		super(200); // 나(자식B2)는 기본, 부모클래스는 매개변수 던지것 호출
		System.out.println("생성자 B");
	}
	B2(int x){ //B2 매개변수를 받는 생성자
		super(x); // 부모 생성자 호출
		System.out.println("생성자 B-param :" + x);
	}
}
public class InheritEx05 {
	public static void main(String[] args) {
		B2 b = new B2();
	}
}

🖥️ console 출력

🔈 부모클래스의 매개변수가 있는 생성자를 실행하고 싶으면 자식클래스에 super() 키워드를 작성하여 호출해주면 된다. 메인메소드를 보면 B2의 기본생성자를 실행하도록 객체를 작성하였다. B2의 기본생성자 안에 부모클래스A2의 매개변수를 200을 던져주는 생성자를 호출하는 코드가 작성되어 있어 A2 매개변수를 받는 생성자가 실행된다. super(200)은 어떤 매개변수가 와도 부모클래스에는 200으로 매개변수가 던저지도록 고정된것이다.

①A2 매개변수를 받는 생성자 ②B2 기본생성자

▶ B2 b = new B2(10); < 이렇게 객체를 생성하면 어떻게 출력될까?

더보기

10이라는 매개변수를 던저 주어 생성자를 실행하기 때문에 B2의 매개변수를 받는 생성자로 가서 그안에 부모클래스이 매개변수를 받는 생성자를 호출하기에 10이 던저져 아래와 같이 출력된다.

 위의 객체생성을 유지한 채 //B2 매개변수를 받는 생성자에 있는 super(x)를 지우면 어떻게 출력될까?

더보기

특별한 수퍼생성자 지정이 없다면 기본생성자가 실행이 됨으로 아래와 같이 출력된다.

 

📖 메소드 오버라이딩 method override 예제

부모로 부터 상속받은 메소드의 내용을 수정하는 것

package day13;
class Super{ // 부모클래스
	void add() {
		System.out.println("Super");
	}
}
class Sub extends Super{ // add() ->@add() // 오버라이딩한것은 앞에 골뱅이를 붙여표현
	// 오버라이딩
	@Override
	void add() { // 부모메소드와 상단부는 동일
		super.add(); // 부모클래스 메소드, 오버라이딩 하였다고 기존의 부모메소드가 훼손되지않음
		System.out.println("Sub"); // 블럭안 구현부만 다름
		System.out.println("Sub");
		System.out.println("Sub");
	}
}

public class InheritEx06 {
	public static void main(String[] args) {
		
		Sub sub = new Sub();
		sub.add();		
	}
}

🖥️ console 출력

🔈 오버라이딩한 메소드 위에 @override 코드를 작성하는 이유는 오버라이딩 했다고 표시하기 위해, 또한 이클립스에서 개발자가 작성하다 오타가 발생했을시 오류로 잡아줌. 가독성도 더 좋음

부모메소드의 내용을 수정하였지만 잘 실행되는걸 확인할 수 있음. 이것이 바로 오버라이딩

📖 다형성 예제(1)

상속관계에서 가능하며 코드를 해설해보면 부모타입의 변수에 자식의 객체를 넣을 수 있는 형태라고 볼 수 있음

다형성 예제를 보기에 앞서 일반형 형변환을 보도록 하자

int a = 10;	// 조상 : 정수
double d = (double)a; // 자손 : 정수, 실수

double d = 5.7;	// 조상 : 정수, 실수
int a = (int)d; // 자손 : int 타입임으로 정수만 사용가능

🔈 int-4byte, double-8byte 작은 크기의 타입을 큰 크기로 바꿀때는 자동으로 형변환이 가능하다. 반대로 형변환 할시 앞에 타입을 써주면 가능하지만 값이 온전히 넣어지지 않는다.

package day13;
class AClass { int x; } // x
class BClass extends AClass { int y; } // x, y

public class PolyEx01 {
	public static void main(String[] args) {
		
		// 다형성 : 부모의 변수에 자식 객체를 담는것
		AClass aa = new BClass();
		// Aclass는 x, BClass는 x,y를 가지고 있음. 여기서 둘다 공통된 x변수를 가지고 있다.
		// 그렇기에 AClass 타입의 변수 aa에 BClass 타입의 객체를 생성할 수 있다.
		// 하지만 AClass는 y를 가지고 있지 않기에 y에 접근은 불가능하다.
		// 가지고 있는것만 접근 가능

		//BClass bb = new AClass(); Error
		// 이유 : 자식클래스는 현재 x,y변수 두개를 가지고 있지만
		// 부모클래스는 x만 가지고 있기 때문에 Bclass 타입의 변수에 객체 생성할 수가 없다.
		// x변수만 가지고 있는건 Aclass 타입은
		// Bclass 타입이라고 할수 없기에 위의 코드는 에러가 난다.
		
		// 기존 객체 생성 방법
		AClass a = new AClass();
		BClass b = new BClass();		
	}
}

📖 다형성 예제(2)

다형성은 어느 클래스가 가능한가 그리고 다형성 했을때 실제 들고 있는 값은 무엇일까요

package day13;
class AClass1 { // 부모클래스로 x, add() 가지고 있음
	int x;
	void add() {
		System.out.println("AAA");
	}
}
class BClass1 extends AClass1{ // 자식클래스로 x, @add(), y, sub()
	int y;
	@Override
	void add() {
		System.out.println("BBB");
	}
	void sub(){
		System.out.println("subsub");
	}
	
}
public class PolyEx02 {
	public static void main(String[] args) {
		//1
		AClass1 a = new AClass1(); // x, add("AAA")
		AClass1 a1 = new BClass1(); // x, @add("BBB"), y, sub()
		
		System.out.println(a.x); // 0 출력
		a.add(); // AAA 출력
		System.out.println(a1.x); // 0 출력
		a1.add(); // BBB 출력 < 오버라이딩 함
		
		//2
		AClass1 a = null;
		BClass1 b = new BClass1();
		a = (AClass1)b;	// AClass1 a = new BClass1(); // 생략가능하지만 명시적으로 적어주자
		System.out.println(a.x);
		a.add(); // a는 AClass1 타입이기 때문에 AClass1의 필드만 실행가능
		System.out.println(b.x);
		System.out.println(b.y); 
		b.add();
		b.sub(); // b는 BClass 타입이기 때문에 BClass1의 필드+ 상속받은 AClass1의 필드 전부 가능
		
		
		//3
		AClass1 a = new AClass1();
		BClass1 b = null;
		b = (BClass1)a; // BClass1 b = new AClass1(); Error
		b.add(); // 이클립스에서 오류가 뜨지 않지만 실행해보면 Error 남!
						// AClass1 타입을 BClass1 타입에 넣을 수 없기 때문에
		
		//4
		AClass1 a = null;
		BClass1 b = new BClass1();
		BClass1 b2 = null;
		a = (AClass1)b; // a는 AClass타입이지만 자식타입도 가지고 있다.
		// 형변환 가능 여부 판단하고 형변환 > 변수 instanceof 타입
		//b2 = a; // 변수만 보면 부모 타입이기 때문에(실제로 들고 있는건 자식값)
		if(a instanceof BClass1) {
			b2 = (BClass1)a; // 형변환 반드시 작성
		}
		b2.sub();
		b2.add();
		
		//5
		AClass1 a = new BClass1(); // a는 BClass1 타입으로 객체 생성됨 실제 들고 있는값은 Aclass것만
		Object o = new BClass1(); // object는 최고 조상님 맨위에 있어서 어떤 객체로든 객체 생성 가능
		Object o1 = new AClass1(); // 어떤 class건 object를 상속 받아 들고 잇기 때문에
		
		BClass1 b = null;
		if(a instanceof BClass1) { // a 위에서 BClass1 타입으로 객체 생성하여 BClass1 타입
			System.out.println("형변환 가능");
			b = (BClass1)a;
		}else {
			System.out.println("형변환 불가능");
		}
	
	}
}

📖 다형성 예제(3)

복잡한 다형성 왜? 있는걸까... 언제 사용되는 걸까... 예제로 이해하기

package day13;

class Person{void func(){System.out.println("funcfunc");}} // func
class Student extends Person{} // func
class Researcher extends Person{} // func
class Professor extends Researcher{} // func

public class PolyEx03 {
	static void print(Person p) {	// Person p = new Person(); // 부모까지 true로 알려줌 // Object
		if(p instanceof Person) {System.out.println("Person");}
		if(p instanceof Student) {System.out.println("Student");} 
		if(p instanceof Researcher) {System.out.println("Researcher");}
		if(p instanceof Professor) {System.out.println("Professor");}
	}
	
	static void test (Object o) { // test > 전달받은 객체의 func()메소드 호출기능
		if(o instanceof Person) { // o 가 실제 들고 있는 객체가 Person이나 Person의 자식이라면
			Person p = (Person)o; // Person 변수로 형변화해서 다시 담고, (옷을 맞게 입혀주기)
			p.func(); // func()를 알아볼 수 있게되어 사용가능해짐
		}
	}

	
	
	public static void main(String[] args) {
		
		System.out.print("new Person -> ");
		print(new Person());
		System.out.print("new Student -> ");
		print(new Student());
		System.out.println("new Researcher -> ");
		print(new Researcher());
		System.out.println("new Professor -> ");
		print(new Professor());
		System.out.println("----------");

		Person p = new Person();
		test(p);
		//test(new Person());
		
		// 다형성 : 부모타입 변수로 자식 객체 담을 수 있다
		//			부모타입 변수로는 부모가 아는 멤버들만 사용가능
		//			온전히 모든 자식 멤버(변수,메소드)를 사용하고 싶으면
		//			자식 변수에 다시 담아줘야 한다 
		//			이때, instanceof  연산자로 형변환 가능한지 체크하고 형변환하기
		//			instanceof 연산자는 자신타입 또는 자신의 자식타입까지 true 리턴해준다
	}
}

🔈 상속 받은 class 정리 (화살표 왼쪽이 자식으로 왼쪽에서 오른쪽으로 상속받았다는 의미)
Student → Person 상속받음 —> 공통으로 맨끝에 최고 조상은 object
Researcher → Person 상속받음
Professor → Researcher → Person 상속받음