재밌고 어려운 IT를 이해해보자~!
상속(Inheritance) 본문
상속이란 ?
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를 사용해 초기화.
코드가 짧아지고 가독성이 좋아지며 강제성이 생긴다. 놓지는 것이 없다.
즉 모든 부모 멤버변수를 메게변수로 가진 생성자를 만들어주고 시작하자!!!!! ~_~
'코리아IT핀테크과정' 카테고리의 다른 글
추상클래스 (abstract class), 인터페이스 (Interface) (2) | 2023.12.06 |
---|---|
상속을 이용한 CRU 설계 및 제작 (0) | 2023.12.03 |
this 예약어, 로그출력, 클래스와 배열의 활용 (0) | 2023.12.01 |
클래스(Class), 메서드(Method), 생성자(Constructor) (0) | 2023.11.30 |
선택정렬 코드 실행 (0) | 2023.11.29 |