1. 상속
- 자바의 모든 클래스는 object 클래스 (부모클래스) 를 상속받음.
- object 클래스는 toString을 갖고 있음. (equals도!)
- toString(): 객체 설명 메서드. 객체의 중요 정보를 하나의 문자열로 반환.
: 여러 클래스가 공통점이 있을 때, 이 공통적인 필드를 뽑아서 상위 클래스로 만들고 이를 상속받도록 만드는 기법.
--- 반복을 줄이고 코드 재사용성을 높이는 방법! ====> 다형성
- 추출한 객체 샘플들 중에서 중복 내용이 많은 객체들을 추출
=> 이 객체의 공통점을 뽑아서 부모 클래스로 만들고, 이 클래스를 상속받아 확장하는 형태로 구현
actor 1 (학생)
- 번호, 이름, 전화, 학과, 수강과목
actor 2 (교수)
- 번호, 이름, 전화, 학과, 개설과목
-------------------------
=> 공통점을 추출해 부모 클래스로 만든다. (원하는 이름으로 만들면 됨)
class Person {
int num;
String name;
String tel;
String dept;
}
// Person을 상속받는 Student 클래스 정의 & 확장 // extends: 클래스 상속 키워드
class Student extends Person {
String[] subjects; // 수강과목
}
// Person을 상속받는 Prof 클래스 정의 & 확장
class Prof extends Person {
String[] open; // 개설과목
}
** JAVA에서는 다중 상속 불가 ==> extends 다음에 부모클래스 1개만 와야함.
(JAVA는 인터페이스를 통한 다중 상속 기능을 제공함.)
* 하나의 부모 클래스를 여러 자식 클래스가 상속받는 것은 가능. ==> 자식클래스 여러개 생성 가능.
* 여러 대를 거쳐 상속 가능 (상속의 상속의 상속....)
* 메서드 재정의 (오버라이딩) : 부모로부터 받은 (상속받은) 메서드를 하위 클래스(자식클래스)에 적합하게 수정해서 사용
-- 메서드 프로토타입(껍데기)(ex. public void f2())은 그대로 두고, 블록 안의 내용만 수정.
-- 하위 클래스(자식)에서 고쳐도 상위 클래스(부모)의 메소드 값은 변하지 않는다.
Source > Override/Implement Methods...
class 액터명 extends 부모클래스명 {
확장할 멤버 변수 정의
}
// 액터: 시스템을 사용할 사람이나 시스템
==========================
class A {
int a;
public void f1() {
System.out.println(a);
}
}
class B extends A {
int b;
@Override // 메서드 오버라이드(재정의)
public void f1() {
System.out.println(a);
System.out.println(a);
}
}
=> B 클래스가 A 클래스를 상속받음.
=> B는 A 클래스의 private과 생성자를 제외한 멤버변수와 메서드를 물려받는다.
=> 즉, 멤버변수 a, b와 재정의된 메서드 f1()를 갖는다.
------------------------main(){}---------------------------
A obj1 = new A();
obj1.f1(); // 부모 메서드 f1() 호출됨 => a만 출력
B obj2 = new B();
obj2.f1(); // 자식 메서드 f1() 호출됨 => a, b 출력
A obj3 = new B(); // 업캐스팅 --- 재정의된 메서드를 호출하면 자식꺼 실행. ~~~ 다형성 구현~
obj3.f1(); // 자식 메서드 f1() 호출됨 => a, b 출력
obj3.a = 10; // 정상 실행
obj3.b = 20; // error!! -- A타입이라 B클래스의 멤버변수 못 씀.
obj3.f2(); // error!! -- A타입이라 B클래스의 메소드 못 씀.
B obj4 = (B) obj3; // 다운캐스팅 --- 업캐스팅 된 것을 원래 타입으로 내리는 캐스팅 -- B클래스의 변수&메소드 쓰고 싶어서.
obj4.b = 20; // 정상 실행. (다운캐스팅 해서!)
obj3.f2(); // 정상 실행. (다운캐스팅 해서!)
예시 1. 상속의 상속의 상속
package inherit;
class GrandParent {
int num;
String name;
public GrandParent() {
System.out.println("GrandParent 생성자");
num = 123;
name = "GrandParent";
}
}
class Parent extends GrandParent {
private int a; // 상속X (클래스 밖에서는 안보이므로)
int b;
public int c;
protected int d; // 같은 패키지에서는 public, 다른 패키지에서는 상속관계 클래스에서만 보임.
public Parent() { // 상속X (생성자는 상속 안됨)
System.out.println("Parent 생성자");
a = 1;
b = 2;
c = 3;
d = 4;
}
public void f1() {
System.out.println("Parent method");
System.out.println("a: " + a);
System.out.println("b: " + b);
System.out.println("c: " + c);
System.out.println("d: " + d);
System.out.println("num: " + num);
System.out.println("name: " + name);
}
}
class Child extends Parent { // 변수: b, c, d, x + 함수: f1, f2
int x;
public Child() {
System.out.println("Child 생성자");
// a = 10;
b = 20;
c = 30;
d = 40;
x = 50;
}
public void f2() {
System.out.println("Child method");
// System.out.println("a: " + a);
System.out.println("b: " + b);
System.out.println("c: " + c);
System.out.println("d: " + d);
System.out.println("x: " + x);
System.out.println("num: " + num);
System.out.println("name: " + name);
}
}
public class PerentMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
GrandParent g = new GrandParent();
Parent p = new Parent(); // 생성자는 객체를 생성할 때에만 호출되며 실행.
p.f1(); //메소드는 호출할 때마다 실행된다.
Child c = new Child();
c.f1();
c.f2();
}
}
=====================================
(print)
GrandParent 생성자
GrandParent 생성자 // 자식객체를 생성하면 자동으로 부모클래스 다 출력됨.
Parent 생성자
Parent method
a: 1
b: 2
c: 3
d: 4
num: 123
name: GrandParent
GrandParent 생성자
Parent 생성자
Child 생성자
Parent method
a: 1 // 부모객체에서 초기화한 값으로 출력 -- private 라 자식객체에서 값 수정 불가.
b: 20 // 자식객체에서 바꾼 값으로 출력
c: 30
d: 40
num: 123
name: GrandParent
Child method
b: 20
c: 30
d: 40
x: 50
num: 123
name: GrandParent
예시 2. 메서드 재정의 (오버라이딩)
package inherit;
class Parent1 {
int a;
public Parent1() {
System.out.println("Parent1 생성자");
a = 10;
}
public void f1() {
System.out.println("a: " + a);
}
}
class Child1 extends Parent1 {
int b;
public Child1() {
System.out.println("Child1 생성자");
b = 20;
}
public void f2() {
// System.out.println("a: " + a);
f1();
System.out.println("b: " + b);
}
}
class Child2 extends Parent1 {
int b;
public Child2() {
System.out.println("Child2 생성자");
b = 20;
}
// Source > Override/Implement Methods...
// 상속받은 메서드를 고쳐서 사용
@Override
public void f1() {
// super: 부모 객체의 참조값
super.f1(); // == System.out.println("a: " + a);
System.out.println("b: " + b); // b까지 출력할 수 있게 수정
}
}
public class OverridingTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Child1 c1 = new Child1();
c1.f1();
c1.f2();
Child2 c2 = new Child2();
c2.f1();
Parent1 p1 = new Parent1();
p1.f1();
}
}
====================================
(print)
Parent1 생성자
Child1 생성자
a: 10
a: 10
b: 20
Parent1 생성자
Child2 생성자
a: 10
b: 20
Parent1 생성자
a: 10
1-1. super
- 부모 객체의 참조값.
--- 오버라이딩된 메서드(자식)에서 부모 객체의 메서드(오버라이딩 전 메서드)를 호출하고 싶을 때 사용.
super.함수명: 부모 객체의 메서드 호출
super(): 부모 객체의 생성자 호출.
- 부모 클래스 생성자가 파라메터가 있는 경우 이 값을 넣어주기 위해 사용.
- 단, 생성자에서 다른 실행문보다 앞서야만 사용 가능.
@Override
public String toString() {
String str = super.toString(); // 부모 객체의 toString()호출
return id + "/" + pwd;
}
class Parent {
int a;
public Parent(int a) {
this.a = a;
}
}
class Child extends Parent {
int b;
public Child(int a, int b) { //자식클래스의 생성자에서 부모클래스의 파라메터값을 넣어줘야함.
// 부모 생성자 호출. 부모클래스 생성자가 파라메터가 있을 때 이 값을 전달하기 위해 사용.
super(a); // 함수. 생성자에서 다른 실행문보다 앞서야 사용 가능.
this.b = b;
}
}
-------------- main{}---------------
Child c = new Child(3, 6);
package inherit;
class Parent3 {
int a;
public Parent3(int a) {
System.out.println("Parent3 생성자");
this.a = a;
}
public void f1() {
System.out.println("Parent3 메서드");
System.out.println("a: " + a);
}
}
class Child3 extends Parent3 {
int b;
public Child3(int a, int b) { //자식클래스의 생성자에서 부모클래스의 파라메터값을 넣어줘야함.
// 부모 생성자 호출. 부모클래스 생성자가 파라메터가 있을 때 이 값을 전달하기 위해 사용.
super(a); // 함수. 생성자에서 다른 실행문보다 앞서야 사용 가능.
this.b = b;
}
@Override
public void f1() {
// TODO Auto-generated method stub
super.f1(); // 부모객체 참조변수.
System.out.println("b: " + b);
}
}
public class SuperTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Child3 c = new Child3(1, 2);
c.f1();
}
}
=====================================
(print)
Parent3 생성자
Parent3 메서드
a: 1
b: 2
1-2. 다형성
: 다양한 모양.
=> 하나의 코드가 실행할 때마다 변신하여 다양한 형태로 실행되는 것.
- 오버로딩, 오버라이딩, 업캐스팅, 다운캐스팅...등을 구현하여 다형성을 만들 수 있다.
2. 업캐스팅 (up-casting)
: 타입이 부모로 올라간 것.
- 상속관계에서만 가능.
- (장점) 오버라이딩한 메서드 호출시 (신 버전)자식 메서드 실행.
- 타입이 부모이기 때문에 부모 클래스에 정의된 멤버 변수와 메서드만 사용 가능.
Parent p = new Child();
package inherit;
class Parent4 {
int a;
public Parent4 () {
System.out.println("Parent4 생성자");
a = 10;
}
public void f1() {
System.out.println("Parent4 메서드");
}
}
class Child4 extends Parent4 {
int b;
public Child4 () {
System.out.println("Child4 생성자");
b = 20;
}
@Override
public void f1() {
// TODO Auto-generated method stub
System.out.println("Child4에서 오버라이딩한 메서드");
}
public void f2() {
System.out.println("Parent4 메서드");
}
}
public class UpcastingTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//up-casting : 타입이 부모로 올라간 것. 상속관계에서만 가능.
Parent4 p = new Child4();
p.f1(); // 오버라이딩한 메서드 호출시 자식 메서드 실행.
// 타입이 부모이기 때문에 부모 클래스에 정의된 멤버만 사용 가능.
p.a = 20;
//p.b = 30; // 자식클래스에 추가된 멤버변수 사용불가.
p.f1(); // 부모클래스에 정의되어 있어서 호출 가능.-- 단, 재정의 된 메서드 실행.
//p.f2(); // 자식클래스에 추가된 메서드 호출불가.
}
}
======================================
(print)
Parent4 생성자
Child4 생성자
Child4에서 오버라이딩한 메서드
Child4에서 오버라이딩한 메서드
3. 다운캐스팅 (down-casting)
: 업캐스팅된 타입을 원래대로 내리는 것. => 자식 클래스에 추가된 멤버변수와 메소드 사용 가능.
- 업캐스팅 된 객체는 부모 타입이라, 부모 클래스에 정의된 멤버변수와 메서드만 사용 가능하다.
----즉, 자식클래스에 추가된 멤버변수와 메소드를 사용 못한다!
-- 자식 클레스의 것들을 사용하고 싶을 때 다운캐스팅을 한다!!!
- 업캐스팅은 캐스팅 연산자 필요 없지만, 다운캐스팅은 캐스팅 연산자 작성해야 함.
Parent p = new Child(); // 업캐스팅
Child c = (Child) p; // 다운캐스팅
((Child)p).f3(); // 다운캐스팅으로 자식클래스 메서드 호출
// up-casting : 타입이 부모로 올라간 것. 상속관계에서만 가능.
Parent4 p = new Child4();
p.f1(); // 오버라이딩한 메서드 호출시 자식 메서드 실행.
// 타입이 부모이기 때문에 부모 클래스에 정의된 멤버만 사용 가능.
p.a = 20;
// p.b = 30; // 자식클래스에 추가된 멤버변수라서 사용불가.
p.f1(); // 부모클래스에 정의되어 있어서 호출 가능.-- 단, 재정의 된 메서드 실행.
// p.f2(); // 자식클래스에 추가된 메서드라서 호출불가.
// 다운 캐스팅
Child4 c = (Child4) p;
System.out.println("c.a: " + c.a);
System.out.println("c.b: " + c.b);
c.f1();
c.f2();
=================================
(print)
Parent4 생성자
Child4 생성자
Child4에서 오버라이딩한 메서드
Child4에서 오버라이딩한 메서드
c.a: 20
c.b: 20
Child4에서 오버라이딩한 메서드
Parent4 메서드
예시1. 업캐스팅과 다운캐스팅을 활용한 다형성 구현
▼ 업캐스팅 사용 전
import java.util.Scanner;
class Car {
String name;
public Car() {
name = "car";
System.out.println("name: " + name);
}
public void horn() {
System.out.println("빵빵");
}
}
class 소방차 extends Car {
public 소방차() {
name = "소방차";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("삐용삐용");
}
}
class 앰뷸런스 extends Car {
public 앰뷸런스() {
name = "앰뷸런스";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("에~~~~엥! 비켜비켜");
}
}
class 경찰차 extends Car {
public 경찰차() {
name = "경찰차";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("저놈 잡아라");
}
}
public class CarMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
System.out.println("자동차 선택: 1.소방차 2.앰뷸런스 3.경찰차");
int x = sc.nextInt();
//업캐스팅 사용 전
switch(x) {
case 1 :
소방차 f = new 소방차();
f.horn();
break;
case 2 :
앰뷸런스 a = new 앰뷸런스();
a.horn();
break;
case 3 :
경찰차 p = new 경찰차();
p.horn();
break;
default:
Car c = new Car();
c.horn();
}
}
}
▼ 업캐스팅 사용 후
import java.util.Scanner;
class Car {
String name;
public Car() {
name = "car";
System.out.println("name: " + name);
}
public void horn() {
System.out.println("빵빵");
}
}
class 소방차 extends Car {
public 소방차() {
name = "소방차";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("삐용삐용");
}
}
class 앰뷸런스 extends Car {
public 앰뷸런스() {
name = "앰뷸런스";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("에~~~~엥! 비켜비켜");
}
}
class 경찰차 extends Car {
public 경찰차() {
name = "경찰차";
System.out.println("name: " + name);
}
@Override
public void horn() {
// TODO Auto-generated method stub
System.out.println("저놈 잡아라");
}
}
public class CarMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
System.out.println("자동차 선택: 1.소방차 2.앰뷸런스 3.경찰차");
int x = sc.nextInt();
// 업캐스팅 사용
Car c = null;
switch (x) {
case 1:
c = new 소방차();
break;
case 2:
c = new 앰뷸런스();
break;
case 3:
c = new 경찰차();
break;
default:
c = new 소방차();
break;
}
c.horn();
}
}
예시2. 상속과 업캐스팅으로 다형성 구현
▼ 메서드 오버로딩만 사용했을 때
package inherit;
class Tv {
String name = "TV";
int price = 100;
int point = 10;
}
class Audio {
String name = "Audio";
int price = 200;
int point = 20;
}
class Computer {
String name = "Computer";
int price = 300;
int point = 30;
}
class Buyer {
int money = 1000;
int point = 0;
// 메서드 오버로딩을 사용해서 제품 구매하는 buy 메서드 구현.
public void buy (Tv p) {
if (money >= p.price) {
money -= p.price;
point += p.point;
System.out.println(p.name + " 구매함");
System.out.println("money: " + money);
System.out.println("point: " + point);
} else {
System.out.println("잔액부족");
}
}
public void buy (Audio p) {
if (money >= p.price) {
money -= p.price;
point += p.point;
System.out.println("money: " + money);
System.out.println("point: " + point);
} else {
System.out.println("잔액부족");
}
}
public void buy (Computer p) {
if (money >= p.price) {
money -= p.price;
point += p.point;
System.out.println("money: " + money);
System.out.println("point: " + point);
} else {
System.out.println("잔액부족");
}
}
}
public class PolyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Buyer b = new Buyer();
b.buy(new Tv());
b.buy(new Audio());
b.buy(new Computer());
b.buy(new Tv());
b.buy(new Audio());
b.buy(new Computer());
b.buy(new Tv());
b.buy(new Audio());
b.buy(new Computer());
}
}
===============================
(print)
TV 구매함
money: 900
point: 10
money: 700
point: 30
money: 400
point: 60
TV 구매함
money: 300
point: 70
money: 100
point: 90
잔액부족
TV 구매함
money: 0
point: 100
잔액부족
잔액부족
▼ 상속, 업캐스팅 사용 후
package inherit;
class Product {
String name;
int price;
int point;
}
class Tv2 extends Product {
public Tv2() {
name = "tv";
price = 100;
point = 10;
}
}
class Audio2 extends Product {
public Audio2() {
name = "audio";
price = 200;
point = 20;
}
}
class Computer2 extends Product {
public Computer2() {
name = "computer";
price = 300;
point = 30;
}
}
class Buyer2 {
int money = 1000;
int point = 0;
public void buy(Product p) {// tv, audio, computer를 업캐스팅으로 받음
if (money >= p.price) {
money -= p.price;
point += p.point;
System.out.println(p.name + " 구매함");
System.out.println("money:" + money);
System.out.println("point:" + point);
} else {
System.out.println("잔액부족");
}
}
}
public class PolyTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 장바구니
Product[] prods = { new Tv2(), new Audio2(), new Computer2(), new Tv2(), new Audio2(), new Computer2(),
new Tv2(), new Audio2(), new Computer2() };
Buyer2 b = new Buyer2();
for (int i = 0; i < prods.length; i++) {
b.buy(prods[i]);
}
}
}
▼ (+) 입력문으로 바꿔봄
package inherit;
import java.util.Scanner;
class Product {
String name;
int price;
int point;
}
class Tv1 extends Product {
public Tv1() {
name = "TV";
price = 100;
point = 10;
}
}
class Audio1 extends Product {
public Audio1() {
name = "Audio";
price = 200;
point = 20;
}
}
class Computer1 extends Product {
public Computer1() {
name = "Computer";
price = 300;
point = 30;
}
}
class Buyer1 {
int money = 1000;
int point = 0;
public void buy(Product p) {
if (money >= p.price) {
money -= p.price;
point += p.point;
System.out.println(p.name + " 구매함");
System.out.println("money: " + money);
System.out.println("point: " + point);
} else {
System.out.println("잔액부족");
}
}
}
public class PolyInherit {
public static void main(String[] args) {
// TODO Auto-generated method stub
Buyer1 b = new Buyer1();
boolean flag = true;
while (flag) {
Scanner sc = new Scanner(System.in);
System.out.println("구매하실 상품을 선택해주세요.");
System.out.println("1.Tv 2.Audio 3.Computer");
int x = sc.nextInt();
Product p = null;
if (b.money > 0) {
switch (x) {
case 1:
p = new Tv1();
b.buy(p);
break;
case 2:
p = new Audio1();
b.buy(p);
break;
case 3:
p = new Computer1();
b.buy(p);
break;
default:
System.out.println("다시 선택해주세요.");
break;
}
if (b.money <= 0) {
System.out.println("잔액부족");
flag = false;
}
} else {
System.out.println("잔액부족");
flag = false;
break;
}
}
}
}