부모 메소드 호출
자식 클래스에서 부모 클래스의 메소드를 재정의하게 되면, 부모 클래스의 메소드는 숨겨지고 재정의 된
자식 메소드만 사용됨
그러나 클래스 내부에서 재정의된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 명시적으로
super 키워드를 붙여서 부모 메소드를 호출할 수 있음
super.부모메소드();
super는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있음
실습 예제
열거 ( 상수 ) enum파일
package sec01.exam04;
public enum FLY {
NORAML, SUPERSONIC
}
부모
package sec01.exam04;
// 부모
public class Airplane {
public void land() {
System.out.println("착륙합니다.");
}
public void fly() {
System.out.println("일반비행합니다.");
}
public void takeOff(){
System.out.println("이륙합니다.");
}
}
자식 ( 상속 받음 )
package sec01.exam04;
//자식 ( 상속 받음 )
public class SupersonicAirplane extends Airplane {
public FLY flymode = FLY.NORAML;//열거
//메소드 재정의
//flyMode가 SUPERSONIC 상수값을 가질 경우에는 true값 실행
public void fly() {
if(flymode == FLY.SUPERSONIC) {//열거
System.out.println("초음속비행합니다.");
}else {
super.fly();// 부모 객체의 fly()메소드 호출
}
}
}
실행
package sec01.exam04;
public class SuperEx {
public static void main(String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.takeOff();
sa.fly();
sa.flymode = FLY.SUPERSONIC; // 열거
sa.fly();
sa.flymode = FLY.NORAML; // 열거
sa.fly();
sa.land();
}
}
final 클래스와 final 메소드
final 키워드는 클래스, 필드, 메소드를 선언할 때 사용할 수 있음
해당 선언이 최종상태이고 결코 수정될 수 없음을 뜻함
final 키워드는 클래스, 필드, 메소드 선언에 사용될 경우 해석이 조금씩 달라짐
클래스와 메소드를 선언할 때 final 키워드가 지정되면 상속과 관련이 있다는 의미임\
상속할 수 없는 final 클래스
클래스를 선언할 때 final 키워드를 class 앞에 붙이면 이 클래스는 최종적인 클래스이므로 상속할
수 없는 클래스가 됨, 즉 final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없다는 것
선언 방식
public final class 클래스 {...}
final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스임
재정의할 수 없는 final 메소드
메소드를 선언할 때 final 키워드를 붙이면 이 메소드는 최종적인 메소드이므로 재정의할 수 없는 메소드가 됨
부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서
재정의할 수 없다는 것
public final 리턴타입 메소드 ( [매개변수, ...] ){ ... }
protected 접근 제한자
protected는 public과 default 접근 제한의 중간쯤에 해당
같은 패키지에서는 default와 같이 접근 제한이 없지만 다른 패키지에서는 자식 클래스만 접근을 허용
protected는 필드와 생성자, 메소드 선언에 사용될 수 있음
패키지 1
package sec01.exam07.pack1;
public class A {
protected String field;
protected A() {
}
protected void method() {
}
}
패키지 1
package sec01.exam07.pack1;
public class B {
public void method() {// 접근가능
A a = new A();
a.field = "value";
a.method();
}
}
패키지 2
package sec01.exam07.pack2;
import sec01.exam07.pack1.A;
public class C {
public void method() {// 패키지 접근 불가
// 생성자 호출 불가
// A a = new A();
// a.field = "value";
// a.method();
}
}
패키지 2
package sec01.exam07.pack2;
import sec01.exam07.pack1.A;
public class D extends A {
public D() { // 상속으로 인해 접근 가능
super();
this.field = "value";
this.method();
}
}
타입 변환과 다형성
자동 타입 변환
타입 변환이란 타입을 다른 타입으로 변환하는 행위를 말함
자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말함
클래스도 마찬가지로 타입 변환이 있는데, 클래스의 변환은 상속 관계에 있는 클래스 사이에서 발생
자식은 부모 타입으로 자동 타입 변환이 가능
자동 타입 변환의 개념은 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있음
위에 예제에서 Cat 클래스로부터 Cat 객체를 생성하고 이것을 Animal 변수에 대입하면 자동 타입 변환이
일어남, 맨 마지막 예제는 메모리 상태를 그림으로 묘사한건데 cat과 animal 변수는 타입만 다를 뿐,
동일한 Cat 객체를 참조, 참조변수의 == 연산은 참조 번지가 같을 경우 true를 산출하므로 두 변수가
동일한 객체를 참조하고 있다는 뜻
cat == animal true
부모
package sec01.verify.exam05;
public class Animal {
}
자식 1
package sec01.verify.exam05;
public class Cat extends Animal {
}
자식2
package sec01.verify.exam05;
public class Dog extends Animal {
}
실행 ( 다형성 )
package sec01.verify.exam05;
public class Ex1 {
public static void main(String[] args) {
// 다형성
Animal a1 = new Cat();
Animal a3 = new Dog();
// Cat a1 = new Cat();
// Animal a2 = a1;
//
//
// Dog a3 = new Dog();
// Animal a4 = a3;
}
}
바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있음
밑에 이미지 참고
다형성
package sec01.verify.exam05;
//다형성
class A{}
class B extends A {}
class C extends A {}
class D extends B {}
class E extends C {}
public class PromotionExample {
public static void main(String[] args) {
B b = new B();
C c = new C();
D d = new D();
E e = new E();
A a1 = b;
A a2 = c;
A a3 = d;
A a4 = e;
B b1 = d;
C C = e;
//B b3 = e;
//C c2 = d;
//상속관계 아님 컴파일에러
}
}
부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능
비록 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정됨
그러나 예외가 있는데, 메소드가 자식 클래스에서 재정의되었다면 자식 클래스의 메소드가 대신 호출
Child 객체는 method3() 메소드를 가지고 있지만, Parent 타입으로 변환된 이후에는 method3()을
호출할 수 없음, 그러나 method2()메소드는 부모와 자식 모두에게 있습니다. 이렇게 재정의된 메소드는
타입 변환 이후에도 자식 메소드가 호출됨
부모
package sec01.verify.exam06;
public class Parent {
public void method1() {
System.out.println("Parent-method1()");
}
public void method2() {
System.out.println("Parent-method2()");
}
}
자식
package sec01.verify.exam06;
public class Child extends Parent {
@Override // 재정의
public void method2() {
System.out.println("Child-method2()");
}
public void method3() {
System.out.println("Child-method3()");
}
}
실행
package sec01.verify.exam06;
public class ChildEx {
public static void main(String[] args) {
Child child = new Child();
// Parent child = new Child();
Parent parent = child; // 자동 형변환
parent.method1();
parent.method2();// 재정의된 메소드가 호출됨
// parent.method3();// 호출 불가능
// 강제 형변환
Child ch = (Child) parent;
ch.method3();
}
}
필드의 다형성
필드의 타입을 부모타입으로 선언하면 다양한 자식 객체들이 저장될 수 있기 때문에 필드 사용 결과가
달라질 수 있다. 이것이 필드의 다형성 / 예) 자동차를 구성하는 부품
프로그램은 수많은 객체들이 서로 연결되고 각자의 역할을 하게 되는데, 이객체들은 다른 객체로 교체될 수
있어야 한다.
부모 클래스를 상속하는 자식 클래스는 부모가 가지고 있는 필드와 메소드를 가지고 있으니 사용 방법이 동일
할 것입니다. 자식클래스는 부모의 메소드를 재정의해서 메소드의 실행 내용을 변경함으로써 더 우수한 실행
결과가 나오게 할 수도 있습니다. 그리고 자식 타입을 부모 타입으로 변환할 수 있습니다.
public class Car {
//필드
Tire frontLeftTire = new Tire("앞왼쪽", 6);
Tire frontRightTire = new Tire("앞오른쪽", 2);
Tire backLeftTire = new Tire("뒤왼쪽", 3);
Tire backRightTire = new Tire("뒤오른쪽", 4);
//생성자
//메소드
int run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
Car 클래스로부터 Car 객체를 생성하면 4개의 Tire 필드에 각각 하나씩 Tire 객체가 들어가게 됩니다.
그런데 frontRightTire와 backLeftTire를 한국타이어와 금호타이어로 교체할 이유가 생겼습니다.
이러한 경우 밑에와 같은 코드를 사용해서 교체할 수 있습니다.
Car myCar = new Car();
myCar.frontRightTire = new HankookTire();
myCar.backLeftTire = new KumhoTire();
myCar.run();
Tire 클래스 타입인 frontRightTire와 backLeftTire는 원래 Tire 객체가 저장되어야 하지만, Tire의 자식 객체가 저장되어도 문제가 없습니다. 왜냐하면 자식 타입은 부모 타입으로 자동 타입 변환이 되기 때문입니다. frontRightTire와 backLeftTire에 Tire 자식 객체가 저장되어도 Car 객체는 Tire 클래스에 선언된 필드와
메소드만 사용하므로 전혀 문제가 되지 않습니다.
HankookTire와 KumhoTire는 부모인 Tire의 필드와 메소드를 가지고 있기 때문입니다.
Car 객체에 run()메소드가 있고, run()메소드는 각 Tire 객체의 roll() 메소드를 밑에 코드 처럼 호출한다고 가정을 해보겠습니다.
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
frontRightTire와 backLeftTire를 교체하기 전에는 Tire 객체의 roll() 메소드가 호출되지만, HankookTire와 KumhoTire로 교체되면 HankookTire와 KumhoTire가 roll()메소드를 재정의하고 있으므로 교체 이후에는 HankookTire와 KumhoTire의 roll()메소드가 호출되어 실행 결과가 달라집니다.
이와 같이 자동 타입 변환을 이용해서 Tire 필드값을 교체함으로써 Car의 run()메소드를 수정하지 않아도 다양한 roll()
메소드의 실행 결과를 얻게 됩니다. 이것이 바로 필드의 다형성입니다.
위의 내용으로 만든 예제 ) 실습
부모 ( Tire )
package sec01.tire.exam01;
public class Tire {
//부모
//필드
public int maxRotation;// 최대 회전수(타이어수명)
public int accumulatedRotation;// 누적 회전수
public String location;// 타이어의 위치
//생성자
public Tire(String location, int maxRotation) {
this.location = location;
this.maxRotation = maxRotation;
}
//메소드
public boolean roll() {
++accumulatedRotation; // 누적 회전수 1 증가
if(accumulatedRotation<maxRotation) {
System.out.println(location + "Tire 수명:" +
(maxRotation-accumulatedRotation) + "회"); //정상 회전(누적<최대)일 경우 실행
return true;
}else {
System.out.println("***" + location + "Tire 펑크 ***");
return false;
}
}
}
부모( Tire )를 부품으로 가지는 클래스 ( Car )
package sec01.tire.exam01;
// 부모( Tire )를 부품으로 가지는 클래스
public class Car {
//필드
Tire frontLeftTire = new Tire("앞왼쪽", 6);
Tire frontRightTire = new Tire("앞오른쪽", 2);
Tire backLeftTire = new Tire("뒤왼쪽", 3);
Tire backRightTire = new Tire("뒤오른쪽", 4);
//생성자
//메소드
int run() {
System.out.println("[자동차가 달립니다.]");
if (frontLeftTire.roll()==false) {stop(); return 1;}
if (frontRightTire.roll()==false) {stop(); return 2;}
if (backLeftTire.roll()==false) {stop(); return 3;}
if (backRightTire.roll()==false) {stop(); return 4;}
return 0;
}
void stop() {
System.out.println("[자동차가 멈춥니다.]");
}
}
Tire의 자식 클래스 ( HankookTire )
package sec01.tire.exam01;
public class HankookTire extends Tire {
//Tire의 자식 클래스
//필드
//생성자
public HankookTire(String location, int maxRotation) {
super(location, maxRotation);
}
//메소드
@Override
public boolean roll() {
++accumulatedRotation;
if(accumulatedRotation<maxRotation) {
System.out.println(location + "HankookTire 수명:" +
(maxRotation-accumulatedRotation + "회"));
return true;
}else {
System.out.println("***" + location + "HankookTire 펑크 ***");
return false;
}
}
}
Tire의 자식 클래스 ( KumhoTire )
package sec01.tire.exam01;
public class KumhoTire extends Tire {
//Tire의 자식 클래스
//필드
//생성자
public KumhoTire(String location, int maxRotation) {
super(location, maxRotation);
}
//메소드
public boolean roll() {
++accumulatedRotation;
if(accumulatedRotation<maxRotation) {
System.out.println(location + " KumhoTire 수명:" +
(maxRotation-accumulatedRotation + "회"));
return true;
}else {
System.out.println("***" + location + "KumhoTire 펑크 ***");
return false;
}
}
}
실행
package sec01.tire.exam01;
public class CarEx {
public static void main(String[] args) {
Car car = new Car();
for(int i=1; i<=5; i++) {
int problemLocation = car.run();
switch (problemLocation) {
case 1:
System.out.println("앞왼쪽 HankookTire로 교체");
car.frontLeftTire = new HankookTire("앞왼쪽", 15);
break;
case 2:
System.out.println("앞오른쪽 kumgoTire로 교체");
car.frontRightTire = new KumhoTire("앞오른쪽", 13);
break;
case 3:
System.out.println("뒤왼쪽 HankookTire로 교체");
car.backLeftTire = new HankookTire("뒤왼쪽", 14);
break;
case 4:
System.out.println("뒤오른쪽 kumgoTire로 교체");
car.backRightTire = new KumhoTire("뒤오른쪽", 17);
break;
}
System.out.println("---------------------------");
}
}
}
'프로젝트 기반 자바(JAVA) 응용 SW개발자 취업과정' 카테고리의 다른 글
2023-06-08 15일차 (0) | 2023.06.08 |
---|---|
2023-06-07 14일차 (0) | 2023.06.07 |
2023-06-01 12일차 (0) | 2023.06.01 |
2023-05-31 11일차 (0) | 2023.05.31 |
2023-05-30 10일차 (0) | 2023.05.30 |