JAVA/Java 기초

메소드 재정의.jav

john_ 2023. 1. 13. 17:43
728x90

2023.01.12 - [JAVA/Java 기초] - 오버라이딩(overriding).java

 

오버라이딩(overriding).java

2023.01.12 - [JAVA/Java 기초] - 상속.java 상속.java 2023.01.11 - [JAVA/Java 기초] - 싱글톤 패턴.java 싱글톤 패턴.java 2023.01.11 - [JAVA/Java 기초] - Getter & Setter .java Getter & Setter .java 2023.01.11 - [JAVA/Java 기초] - 패키

less-go.tistory.com

 

이전글에서 부터 계속됩니다.

 


부모 메소드 호출

  • 자식 메소드 내에서 super 키워드와 도트(.) 연산자를 사용하면 숨겨진 부모 메소드를 호출 합니다.
  • 부모 메소드를 재사용 함으로써 자식 메소드의 중복 작업 내용을 없애는 효과가 있습니다.

 


package java230113;
//부모 클래스 : Airplane
public class Airplane {
	
	public void land() {
		System.out.println("착륙합니다.");
	}
	
	public void fly() {
		System.out.println("일반 비행합니다.");
	}
	
	public void takeOff() {
		System.out.println("이륙합니다.");
	}
}
package java230113;
//자식 클래스 : SupersonicAirplane
public class SupersonicAirplane extends Airplane{

	public static final int NORMAL = 1;
	public static final int SUPERSONIC =2;
	
	public int flyMode = NORMAL;
	
	@Override
	public void fly() {
		if(flyMode == SUPERSONIC) {
			System.out.println("초음속 비행합니다.");
		}else {
			super.fly();
		}
	}
}
package java230113;
//실행 소스
public class SupersonicAirplaneExam {

	public static void main(String[] args) {

		SupersonicAirplane sa = new SupersonicAirplane();
		
		sa.takeOff();
		sa.fly();
		sa.flyMode = SupersonicAirplane.SUPERSONIC;
		sa.fly();
		sa.flyMode = SupersonicAirplane.NORMAL;
		sa.fly();
		sa.land();
	}
}
결과 :
이륙합니다.
일반 비행합니다.
초음속 비행합니다.
일반 비행합니다.
착륙합니다.

 


final 클래스

  • final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들수 없습니다.
public final class 클래스명 {...}

 

final 메소드

  • 메소드를 선언 할때 final키워드를 붙이면 오버라이딩 할수 없습니다.
  • 부모클래스를 상속해서 자식클래스를 선언 할때, 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의 할수 없습니다.
public final 리턴타입 메소드명(매개변수, ...) {...}
ex)

public final class Member {
}

public class VeryImportantPerson extends Member{	// 에러발생
//Member가 final로 고정되어 자식클래스를 만들수 없습니다.
	
}

Car 클래스에서 stop()메소드를 final로 선언하면?

package java230113;
//부모클래스 : Car
public class Car {

	public int speed;
	
	public void speedUp() {
		speed+=1;
	}
	public final void stop(){
		System.out.println("차를 멈춤");
		speed = 0;
	}
}
package java230113;
//자식클래스 : sportscar
public class SportsCar extends Car{

	@Override
	public void speedUp() {
		speed +=10;
	}
	
	@Override
	public void stop() {	//상속이 안됨, 재정의 불가
		System.out.println("스포츠카를 멈춤.");
		speed = 0;
	}
}

 


protected 접근 제한자

  • protected는 상속과 관련있고, public과 default의 중간쯤에 해당합니다.
  • protected는 같은 패키지에서는 default 처럼 접근이 가능하지만, 다른패키지에서는 자식클래스만 접근이 가능합니다.

protected는 필드, 생성자, 메소드를 제한합니다.

  • protected는 같은 패키지에서 사용하거나, 다른 패키지에서의 자식객체만 사용이 가능합니다.
package1

//부모클래스
public class A {	
	protected String field;
	protected A() {}
	protected void method() {}
}
//동일 패키지
public class B {
	public void method() {
		A a = new A();
		a.field = "value";
		a.method();
	}
}
//다른 패키지
package2

import package1.A;  //패키지 1번의 A클래스 임포팅
public class C {
	A a = new A();		//a에 protected : 다른 패키지라서 접근이 불가능
	a.field = "value";
	a.method();
}
//다른패키지, 자식클래스
import package1.A; // 패키지 1번의 A클래스 임포팅

public class D extends A{
	public D() {
    	//A() 생성자 호출
		super();
	}
	public void method1() {
    	//A 필드값 변경
		this.field = "value";
        //A 메소드 호출
		this.method();
	}
    //method2는 사용불가능...
	public void method2() {
    	//new 연산자를 사용해 호출은 불가능.
        //자식 생성자에서 super()로 A생성자 호출은 가능합니다.
		A a = new A();
		a.field = "value";
		a.method();
	}
}

 


 

자동 타입 변환

  • 자동적으로 타입 변환이 일어나는 것입니다.

 

 

  • 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급됩니다.
  • 예를들어 고양이가 동물의 특징과 기능을 상속 받았다면 '고양이는 동물이다'가 성립합니다.

 

package java230113;

class A{}
class B extends A{}
class C extends A{}
class D extends B{}
class E extends C{}

public class PromotionExam {

	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 c1 = e;
		
		B b2 = e;	// 에러 : 상속관계가 아닙니다.
		C c2 = d;
		
	}
}

 


package java230113;
//부모 클래스
public class Parent {

	public void method1() {
		System.out.println("Parent - method1()");
	}
	
	public void method2() {
		System.out.println("Parent - method2()");
	}
}


//자식 클래스
public class Child extends Parent{

	@Override
	public void method2() {
		System.out.println("Child-method()");
	}
	
	public void method3() {
		System.out.println("Child-method3()");
	}
}

//실행소스코드
public class ChildExam {

	public static void main(String[] args) {

		// 자식 객체 생성
		Child child = new Child();
		
		// 자동 타입 변환
		Parent parent = child;
		//child 의 객체를 가져와서 parent 객체를 생성합니다.
		//child==3개의 메소드
		//parent==2개의 메소드
		//메소드의 개수가 맞지않습니다.
		//따라서 child의 메소드 3이 없는 parent 객체를 만들어
		//parent에서 상속받은 method1과
		//child에서 overriding된 method2만 남긴다
		
		parent.method1();	// Parent-method1
		parent.method2();	// Child-method2
//		parent.method3();
	}
}

 


강제 타입 변환

  • 부모 타입은 자식 타입으로 자동 변환되지 않습니다.
  • 대신 캐스팅 연산자로 강제 타입 변환이 가능합니다.

//부모 클래스
public class Parent {

	public String field1;
	
	public void method1() {
		System.out.println("Parent-method1()");
	}
	
	public void method2() {
		System.out.println("Parent-method2()");
	}
}
//자식 클래스
public class Child extends Parent{

	public String field2;
	
	public void method3() {
		System.out.println("Child-method3()");
	}
}
//실행파일
public class ChildExam {

	public static void main(String[] args) {

		// 자동 타입 변환
		Parent parent = new Child();
		
		parent.field1 = "data1";
		parent.method1();
		parent.method2();
		
//		parent.field2 = "data2";
//		parent.method3(); 
		//부모타입으로 변환할때  부모클래스에는
		//field2, method3이 없으므로 에러발
		
		// 강제 타입 변환
		Child child = (Child) parent;
		// 부모타입에 f2, m3() 붙여져 타입변환이 됩니다.
		
		child.field2 = "data2";
		child.method3();
	}
}
결과 : 
Parent-method1()
Parent-method2()
Child-method3()

 


다형성

  • 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질입니다.
  • 다형성을 구현 하기 위해서는 자동 타입 변환과 메소드 재정의가 필요합니다.

 

 

필드 다형성

  • 필드 타입은 동일하지만, 대입되는 객체가 달라져서 실행 결과가 다양하게 나올수 있는 것입니다.

 

예제로 확인을 해보겠습니다.

//부모클래스
public class Tire {

	public void roll() {
		System.out.println("회전합니다.");
	}
}



//Tire를 상속받는 자식클래스
public class HankookTire extends Tire {

	@Override
	public void roll()	{
		System.out.println("한국 타이어가 회전합니다.");
	}
}



//Tire를 상속받는 자식클래스
public class KumhoTire extends Tire{

	@Override
	public void roll() {
		System.out.println("금호 타이어가 회전합니다.");
	}
}


public class Car {
	//필드선언
	public Tire tire;
	
	public void run() {
    	//tire 필드에 대입된 객체의 roll() 메소드 호출
		tire.roll();
	}
}


//실행부
public class CarExam {

	public static void main(String[] args) {
		
		// Car 객체 생성
		Car myCar = new Car();
		
		// Tire 객체 장착
		myCar.tire = new Tire();
		myCar.run();	// 타이어 롤 실행 : 타이어 회전
		
		// HankookTire 객체 장착
		myCar.tire = new HankookTire();
		myCar.run();	// 타이어 롤 실행 : 타이어 회전
		
		// KumhoTire 객체 장착
		myCar.tire = new KumhoTire();
		myCar.run();	// 타이어 롤 실행 : 타이어 회전
		
	}
}
결과 : 
회전합니다.
한국 타이어가 회전합니다.
금호 타이어가 회전합니다.

 

 


매개변수 다형성

  • 메소드가 클래스 타입의 매개변수를 가지고 있을 경우, 호출할 때 동일한 타입의 자식 객체를 제공할수 있습니다.
  • 어떤 자식 객체가 제공되느냐에 따라 메소드의 실행결과가 달라집니다.
//다음과 같이 클래스가 정의 되어있을때,
public class Driver{
	public void drive(Vehicle vehicle){
    	vehicle.run();
    }
}



Driver driver = new Driver();
Vehicle vehicle = new Vehicle();
driver.drive(vehicle);

//실행소스코드부에서 위와 같이 drive메소드를 호출할경우
//vehicle 뿐만아니라 자동 타입변환으로 인해 Vehicle의 자식 객체도 제공이 가능합니다.

 

 

예제와 설명을 통해 다시 확인해보겠습니다.

 

//부모 클래스 Vehicle
public class Vehicle {
	//메소드 선언
	public void run() {
		System.out.println("주행합니다.");
	}
}
//자식클래스 Taxi, 부모클래스 Vehicle
public class Taxi extends Vehicle{
	//메소드 오버라이딩(재정의)
	@Override
	public void run() {
		System.out.println("택시가 달립니다.");
	}
}
//자식클래스 Bus. Vehicle로 부터 상속
public class Bus extends Vehicle{
	//메소드 재정의(오버라이딩)
	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
}
public class Driver {
	//메소드 선언 ( 클래스타입의 매개변수를 받아옵니다)
	public void drive(Vehicle vehicle) {
		vehicle.run();
	}
}
//사용 소스코드부
public class DriverExam {

	public static void main(String[] args) {

		Driver driver = new Driver();

		Bus bus = new Bus();
		driver.drive(bus);
		
		Taxi taxi = new Taxi();
		driver.drive(taxi);
	}
}
결과 : 
버스가 달립니다.
택시가 달립니다.

 

상기 정의된 drive() 메소드는 매개변수 vehicle이 참조하는 객체의 run() 메소드를 호출합니다.
자식객체가 run() 메소드를 재정의 하고 있다면 재정의된 run() 메소드가 호출됩니다.
따라서 어떠한 자식객체가 제공 되느냐에 따라서 drive()의 실행결과가 달라집니다.
이것이 매개변수의 다형성입니다.

 

 


객체 타입 확인

  • instanceof : 생성된 객체의 타입을 확인하고자 할때 사용합니다.
  • 매개 변수가 아니더라도 변수가 참조하는 객체의 타입을 확인할 때 instanceof 연산자를 사용합니다.
  • instanceof 연산자에서 좌항의 객체가 우항의 타입이면 true를, 그렇지 않으면 false를 반환합니다.
boolean result = 객체 instanceof 타입;
public void method(Parent parent){
	if(parent instanceof Child){
    	//parent 매개변수가 참조하는 객체가 Child인지 조사합니다.
    	Child child = (Child)parent;
        //강제타입변환 하는 이유는 Childe 객체의 모든 멤버(필드,메소드)에 접근하기
        //위해서 입니다.
        //child 변수 사용
    }
}

 

  • Java 12 부터는 instanceof 연산의 결과가 true일 경우, 우측 타입 변수를 사용할수 있기 때문에 강제 타입변환이 필요 없습니다.

예시를 들어보겠습니다.

public class InstanceofExample {
	
	// 정적 메소드 생성 : main()메소드에서 바로 호출해서 사용하기 위해 정적 메소드 선언.
	public static void personInfo(Person person) {
		System.out.println("name : " + person.name);
		person.walk();
		
//	person이 참조하는 객체가 Student 타입인지 확인
		if (person instanceof Student) {	// 사용자가 생성한 타입이 Student 타입인지
			Student student = (Student) person;	//student객체를 부모 person이 객체를 이용해 생성
			System.out.println("studentNo: " + student.studentNo); //학번출력
			student.study();	// student의 study 메소드 호출
			
		}
	}
    
//  JAVA12 이후에 아래 방식 사용
//	if(person instanceof Student student) {
//		System.out.println("StudentNo: " + student.studentNo);
//		student.study();
//	}
//	
	
	public static void main(String[] args) {
		
		//Person 객체를 매개값으로 제공하고 personInfo() 메소드 호출
		Person p1 = new Person("홍길동");
		personInfo(p1);
		
		System.out.println();
		
		//Student 깨체를 매개값으로 제공하고 personInfo() 메소드 호출
		Person p2 = new Student ("김길동", 10);
		personInfo(p2);
		
	}
}
// 부모 클래스인 person 에는
// 필드명인 name
// 생성자 person(String name)
// 메소드 walk() 가 포함됩니다.
public class Person {
	
	//필드 선언
	public String name;
	
	//생성자 선언
	public Person(String name) {
		this.name=name;
	}
	
	//메소드 선언
	public void walk() {
		System.out.println("걷습니다.");
	}
}
// 자식클래스인 Student 에는
// 필드 studentNo;
// 생성자 Student(String name, int studnetNo){}
// 메소드 study()가 포함되어 있습니다.
public class Student extends Person {

	public int studentNo;
	
	public Student(String name, int studentNo) {
		super(name);
		this.studentNo = studentNo;
	}
	
	public void study() {
		System.out.println("공부를 합니다.");
	}
}
결과 :
name : 홍길동
걷습니다.

name : 김길동
걷습니다.
studentNo: 10
공부를 합니다.
728x90

'JAVA > Java 기초' 카테고리의 다른 글

인터페이스(Interface).java  (0) 2023.01.16
추상클래스.java  (0) 2023.01.16
오버라이딩(overriding).java  (0) 2023.01.12
클래스와 인스턴스 등...(리마인드용)  (0) 2023.01.12
상속.java  (1) 2023.01.12