재밌고 어려운 IT를 이해해보자~!

상속(Inheritance) 본문

코리아IT핀테크과정

상속(Inheritance)

언제나즐거운IT 2023. 12. 1. 21:28

상속이란 ?

class B extends A{
	int data01 = 20;
	void funcB() {
		System.out.println(this.data01+"메서드03");
	}
}

Class B extends A

"B가 A로부터 상속을 받는다." 라는 뜻이다.

상속관계는 extends 키워드 사용.

 

상속을 받게되면 개념적으로 포함관계 형성된다. a객체와 b객체 사이에 형성됨
모든  B객체들은 클래스A의 특징을 갖게된다. 클래스 A 개념이 보다더 넓은 개념.
프로그램에는 이런 개념이 굉장히많다.


ex)
모양: 원,네모,세모
사람: 학생,사원
카드: 신용카드..체크카드,교통카드
동물: 강아지,고양이
포켓몬: 전기, 물, 불, 꼬부기,고라파덕, 잉어킹

 

자바에선
클래스A를 상위 클래스 or 부모 클래스
클래스B를 하위 클래스 or 자식 클래스

파이썬은 -> 파생클래스 라고 호칭! 

 

다음 코드를 보자...

package class01;

class Point {
	int x;
	int y;
	Point(){
		this(0,0);
		System.out.println("로그1");
	}
	Point(int x,int y) {
		this.x=x;
		this.y=y;
		System.out.println("로그2");
	
	}
	void move(int x) {
		this.x+=x;
		this.y+=x;

	}
	void printInfo() {
		System.out.println("점 ("+this.x+","+this.y+")입니다.");
	}
}

//보통 자식클래스를 아래에 작성
//색갈 점은 점의 한 종류다~_~ 점에서 가져오자
class ColorPoint extends Point {
	//점이 갖고있지 않았던거만 구현하자 !!!!
	String color;
	ColorPoint(String color) {
		this(color,0,0);
		System.out.println("로그3");
	}
	
	ColorPoint(int x, int y) {
		this("검정",x,y);
		System.out.println("로그4");
	}
	
	ColorPoint(String color, int x, int y) {
		
		//댕헷갈림!!!!!!!!
		//모든 자식 클래스의 생성자 가장 첫줄에는 부모클래스의 기본생성자가 자동호출된다!!!!!
		//--> 부모의 기본생성자 호출 떄문에 로그 215 찍힌다!!!
		
		super(); //-> 부모()라는 뜻이며 부모 == 부모 생성자 함수
		this.color=color;
		this.x=x;
		this.y=y;
		System.out.println("로그5");
		
	}
	
}
// 심화필기~ why ?
//붕어빵틀 붕어빵
//클래스로 객체 생성
//부모클래스의기본생성자 자동호출 이유 ?
//자식클래스에는 부모클래스가 가지고있는 멤버변수를 초기화할수있는능력이 없어서 ㅠㅠ
//부모클래스의 기본생성자를 호출해야만 자식클래스가 부모클래스의 멤버변수를 초기화할수있다.

//생성자의 역할이 멤버변수 초기화인데,
// 부모클래스가 갖고있었던 멤버변수를 초기화하는 역할을 부모 클래스의 생성자로 갖고있으니...
//호출할 수 밖에 없다. 메모리 원리 때문에 모든언어 다 적용된다. 



public class Test03 {
	public static void main(String[] args) {
		ColorPoint cp= new ColorPoint("분홍",1,2);
		cp.printInfo();
		cp.move(10);
		cp.printInfo();
	}
}

 

보통 자식클래스를를 아래에 구성하며 모든 자식클래스 첫줄에는 부모클래스의 기본생성자가 자동호출된다!

super()  <-- 부모클래스의 기본생성자!

자식클래스를 사용할때 부모클래스의 멤버변수도 초기화를 꼭 해야하는데, 자식은  가지고있는 멤버변수를 초기화 할 수 있는 능력이 없다. 생성자의 역할이 멤버변수 초기화인데 부모 클래스 생성자는 자식이 없으니... 호출할 수 밖에 없다!!!

 

다음코드를보자..

package class01;

class Pokemon{
	String type;
	Pokemon(String type) {
		this.type=type;
	}
	void printInfo() {
		System.out.println(this.type);
	}
}

class Pikachu extends Pokemon{
	String name;
	Pikachu() {
		this("피카츄");
	}
	Pikachu(String name) {
		//super(); -> 부모의 기본생성자가 없어서 자동으로 호출못한다ㅠ 
		//해결방안
		//1.부모 클래스에게 기본생성자를 만들어주는줌 --> 이거 사용하면안된다!!!!
		// 자식을 만들때에러가나서 부모를 건들이는것은, 설계를 안했거나 못했거나 하는것이다.
		// 개발을하다가 이전내용을 변경해야하는 일이생기면 아~내가 설계 부족하구나 !!!
		// 코드작성을 멈추고 설계를 다시 점검
		
		//2. 부모에게 존재하는 다른 생성자를 호출해서  부모클래스에 선언된 멤버변수를 초기화한다!!!!!!
		//기본생성자말고 있는생성자 사용하자. 멤버변수만 초기화하면 된다.
		//모든 멤버변수를 초기화 할수있다면. 에러가 안난다!!!!!!!!!!!
		super("전기");
		this.name =name;
	}
}


public class Test04 {
	public static void main(String[] args) {
	
	}
}

 

먄약 부모클래스에 메게변수를 가진 생성자가  선언되어있어서 기본생성자가 없다면 ?!

부모클래스에 기본생성자를 직접 선언하는 방법도 있지만 절때금지 ! 설계가 부족한것. 애초에 만들어놓던지!!

 

부모에게 존재하는 생성자만을 사용해서 초기화 하면된다.

초기화만 한다면 에러가안난다.

부모클래스인 Pokemon은

Pokemon(String type) {
  this.type=type;

}

생성자를 가지고 있기 때문에

 

super("전기"); 로 부모클래스 멤버변수 초기화 끝~!

 

오버라이딩(Overriding)

package class02;

class Point {
	int x;
	int y;
	Point(){
		this(0,0);
	}
	
	Point(int x) {
		this(x,x);
	}
	
	Point(int x, int y) {
		this.x=x;
		this.y=y;
	}
	void move(int x) {
		this.x+=x;
		this.y+=x;
	
	}
	
	void printInfo() {
		System.out.println("점이 ("+this.x+","+this.y+")");
	}
}

class ColorPoint extends Point {
	String color;
	
	ColorPoint() {
		this(0,0);
		//생성자에서 부모메서드 호출 하지말자.. 가능은함
		// 생성자는 멤버변수 초기화가 목적.
	}
	ColorPoint(String color) {
		this(color,0,0);
	}
	ColorPoint(int x,int y) {
		this("검정",x,y);
	}
	//가장 이상적 활용
	ColorPoint(String color,int x,int y) {
		super(x,y); //부모생성자를 이용한 xy값 초기화 그이후 이 생성자를 이용한 메게변수가 작은 생성자 초기화
		this.color=color;
	}
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		super.printInfo(); //부모클래스의 메서드 가져와서 재정의~
		System.out.println(this.color+"점이 ("+this.x+","+this.y+")");
	}
	
//	void printInfo() {
//		System.out.println(this.color+"점이 ("+this.x+","+this.y+")");
//	} // 기존에 부모가 사용하던 메서드는 더이상 사용 X 재정의를 했다.
	
	// 정리!
	// 오버로딩은 함수명이동일하고 메서드 시그니쳐가 다른것 > 상속관계 X 상속에서 안배움
	// 함수명 중복정의 허용
	// 함수가 N개
	
	//vs 오버라이딩
	// 함수명이동일
	//메서드 시그니쳐가 같음
	//상속 관계 O
	//메서드 재정의
	// 메서드가 1개 ---> 팀편성 시험에 나온다.
		
	
}



public class Test01 {
	
	public static void main(String[] args) {
	//자식클래스 입장에서 
	//부모클래스가 가진 printInfo()라는 기능을 사용하고는 싶은데... 뭔가 동작방법이...내마음에 안드네 ..?
	

	// => 메서드 재정의 : 기존에 부모가 사용하던 메서드는 더이상 사용 X
		ColorPoint cp1 = new ColorPoint();
		ColorPoint cp2 = new ColorPoint("분홍");
		ColorPoint cp3 = new ColorPoint("노랑",1,1); 
		
		Point p1 = new Point();
		p1.printInfo();
		cp1.printInfo();
		cp2.printInfo();
		
		cp2.move(1);
		cp2.printInfo();
		cp3.printInfo();
		
		
		
	}
}

 

부모의 메서드 자식이 쓰기좋게 재정의!

* 오버로딩은 함수명이동일하고 메서드 시그니쳐가 다른것! 상속관계 X 

 

상속 두번씩하기 ~_~

package class02;


class Pokemon {
	String type;
	int lv;
	int exp;
	String name;
	Pokemon(String type) { //이놈으로 객체를 만들 마음은 없고 type 멤버변수 값을 강제하기 위함
		this.type=type;
		this.lv=5;
		this.exp=0;
		this.name="포케몬";
	}
	
	void attack() {
		System.out.println(" [ 로 그 ] ");
		System.out.println("포켓몬 클래스의 attack()");
		System.out.println("아직 구현 X");
	}
	
	void hello() {
		System.out.println(" [ 로 그 ] ");
		System.out.println("포켓몬 클래스의 hello()");
		System.out.println("아직 구현 X");
	
	}
}

class Elec extends Pokemon {

	Elec() {
		super("전기");
		
	}

	@Override // @ annotation(어노테이션) : 코드 가독성 향상, 메모리 성능 향상, 주로 설정 사항에 대해서 추가
	//컴터가 얘를 제일먼저 확인, 바로 재정의된걸 알수있음
	void attack() {
		System.out.println("백만만볼트 !!!~~~~~");
	}
	
}

class Picachu extends Elec {
	Picachu() {
		this("피카츄");
	}
	Picachu(String name) {
		this.name = name;
	}
	@Override
	void hello() {
		System.out.println("피카피카 -_-!!");
	}
	
}

class Water extends Pokemon {
	Water() {
		super("물타입");
	}

	@Override
	void attack() {
		// TODO Auto-generated method stub
		System.out.println("물대포!!!!~~");
	}
}
class Squirtle extends Water {
	Squirtle() {
		this("꼬부기");
	}
	Squirtle(String name){
		this.name=name;
	}
	@Override
	void hello() {
		// TODO Auto-generated method stub
		System.out.println("꼬북꼬북!!!!!");
	}
	
}

public class Test02 {

		public static void main(String[] args) {
			
			Picachu pika= new Picachu();
			Squirtle sq = new Squirtle();
			pika.attack();
			pika.hello();
			sq.attack();
			sq.hello();
			// 가장 최근에 정의된(오버라이딩된) 메서드가 자동호출됨
			// == 다형성이 실현되었다. 여러개의 상태 ....
			
			// 클래스는 자료형이다!
			// 몬스터볼 6개 포켓몬 6개 
//			picachu[] datas = new picachu[6] --> 피카츄만 데려다님
//			pokemon[] datas2=new pokemon[6] 이라 해야  다양한 포켓몬 데리ㅏ고다님
//			datas[0] = new picachu();
//			
//			ex)
//			dobule[] datas=new double[6];
//			datas[0] =10; -> 더큰 개념안에 작은개념이 들어갈수있다
		}
	}

항상 자식 클래스에는 부모클래스의 기본생성자가 있다는걸 잊지말자.

처음 자식 클래스를 선언해서 공백으로 놔두면 에러가 나올 수 있다!

 

 

형변환, Instanceof

학생과 직장인을 담은 사람배열에서 학생만의 데이터를 뽑아서 학생클래스에 들어있는 메서드를 쓸려면 ?!

Person[] datas=new Person[5]; 

사람 객체는 datas! 

datas[0] instanceof Student 를 하면 !! True false 를 반환하는데 

datas 라는 정보가 학생클래스에 관련된 것들에 대해서만 True 가 나온다!!! 직장인은 false~

datas[0] instanceof Person을 하면 All true!!

 

이제 학생만 가지고있는 메서드 test()를 사용해보자

Student stu = (Student)data[0];

을 하면 !! Person클래스로 선언된 data객체가 Student클래스로 형변환되어서 

stu 객체에 담긴다!

 

이제 호출 가능 ~~~ stu.test();  !

-----------------

[문제]

사람
   이름 name
   나이 age
   printInfo() : ㅇㅇ님 ㅇㅇ살
학생
   성적 score
   printInfo() : ㅇㅇ학생 ㅇㅇ살 ㅇㅇ점
   test() : 기존점수이상 100점이하 점수 재설정
직원 Emp
   부서 dep
   printInfo() : ㅇㅇ팀 ㅇㅇ님 ㅇㅇ살

C   사람 추가
      학생 추가
      직원 추가
R   사람목록 출력
U   학생목록 출력 후 전체 재시험
프로그램 종료

-------------------------

package class02;

import java.util.Random;
import java.util.Scanner;

class Person {
   String name;
   int age;
   Person(String name,int age){
      this.name=name;
      this.age=age;
   }
   void printInfo() {
      System.out.println(this.name+"님 "+this.age+"살");
   }
}
class Student extends Person {
   int score;
   Student(String name,int age){
      this(name,age,new Random().nextInt(101));
   }
   Student(String name,int age,int score){
      super(name,age);
      this.score=score;
   }
   @Override
   void printInfo() {
      System.out.println(this.name+"학생 "+this.age+"살 "+this.score+"점");
   }
   void test() {
      this.score=new Random().nextInt(101-this.score)+this.score;
      System.out.println(this.name+"학생이 재시험을 봤습니당~^^");
   }
}
class Emp extends Person {
   String dep;
   Emp(String name,int age){
      this(name,age,"인턴");
   }
   Emp(String name,int age,String dep){
      super(name,age);
      this.dep=dep;
   }
   @Override
   void printInfo() {
      System.out.println(this.dep+"팀 "+this.name+"님 "+this.age+"살");
   }
}

public class Test04 {

   public static void main(String[] args) {

      Person[] datas=new Person[5];
      int index=0;

      Scanner sc=new Scanner(System.in);

      while(true) {
         System.out.println("1. 사람 추가");
         System.out.println("2. 사람 목록 출력");
         System.out.println("3. 전체 재시험");
         System.out.println("4. 프로그램 종료");
         System.out.print("입력 >> ");
         int action=sc.nextInt();
         if(action==1) {
            while(true) {
               System.out.println("1. 학생 추가");
               System.out.println("2. 직원 추가");
               System.out.print("입력 >> ");
               action=sc.nextInt();
               if(1<=action && action<=2) {
                  break;
               }
               System.out.println("다시입력해주세요!");
            }
            if(action==1) { // 학생 추가
               System.out.print("이름입력 >> ");
               String name=sc.next();
               System.out.print("나이입력 >> ");
               int age=sc.nextInt();
               // 성적입력 >> 
               datas[index++]=new Student(name,age);
            }
            else if(action==2) { // 직원 추가
               System.out.print("이름입력 >> ");
               String name=sc.next();
               System.out.print("나이입력 >> ");
               int age=sc.nextInt();
               // 부서입력 >> 
               datas[index++]=new Emp(name,age);
            }
         }
         else if(action==2) {
            if(index<=0) {
               System.out.println("데이터없음!");
               continue;
            }
            for(int i=0;i<index;i++) {
               datas[i].printInfo();
            }
         }
         else if(action==3) {
            for(int i=0;i<index;i++) {
               if(datas[i] instanceof Student) {
                  datas[i].printInfo();
                  Student stu=(Student)datas[i]; // 강제 형변환, 캐스팅
                  stu.test();
               }
            }
         }
         else {
            break;
         }
      }
   }
}

 

상속관계에서 생성자 생성 팁

부모가 가진 멤버변수를 모두 초기화 하고 시작하는 것이 좋다.

 

내려가면서 자식이 super를 사용해 초기화.


코드가 짧아지고 가독성이 좋아지며 강제성이 생긴다. 놓지는 것이 없다.


즉 모든 부모 멤버변수를 메게변수로 가진 생성자를 만들어주고 시작하자!!!!! ~_~

Comments