* JAVA - 오버라이딩

jodbsgh·2022년 3월 20일
0

💡"JAVA"

목록 보기
19/67

오버라이딩이란?

  • 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 함.
class Point{
	int x;
    int y;
    
    String getLocation(){
    	return "x:" + x + ", y:" + y;
    }
}

class Point3D extends Point{
	int z;
    
    String getLocation(){ 		//오버라이딩
    	return "x:" + x + ", y:" + y+ ", z:"+ z;
    }

}

이 두 클래스는 서로 상속관계에 있으므로 Point3D 클래스는 Point클래스로 부터 getLocation()을 상속받지만, Point3D 클래스의 성격과 맞지 않는다. 그래서 Point3D에 알맞게 오버라이딩 했다.

오버라이딩의 조건

메서드의 선언부는 조상의 것과 완전히 일치해야 한다.

  • 이름이 같아야한다.
  • 매개변수가 같아야한다.
  • 반환타입이 같아야한다.

한마디로 요약하면 선언부가 서로 일치해야 한다.

  1. 접근 제어는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
    ex) 조상클래스의 접근 제어자가 protected라면 이를 오버라이딩 하는 자손 클래스의 메서드는 protected 또는 public 이어야 함.

  2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
    아래 코드를 보면 Child클래스의 parentMethod()에 선언된 예외의 개수가 조상인 Parent클래스의 parentMethod()에 선언된 예외의 개수보다 적으므로 바르게 오버라이딩 되었다.

class Parent{
	void parentMethod() throws IOException, SQLException{
    
    }
}

class Child extends Parent{
	void parentMethod() throws IOException{
    
    }
}
  • 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
  • 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
  • 인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

오버로딩 vs 오버라이딩

오버로딩 : 기존에 없는 새로운 메서드를 정의하는 것 (new)
오버라이딩 : 상속받은 메서드의 내용을 변경하는 것(change, modify)

super

  • 상속받은 멤버와 자신의 멤버와 이름이 같을 때는 super르 ㄹ붙여서 구별할 수 있다.
  • 조상 클래스의 멤버와 자손클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우만 super를 사용하는 것이 좋다.
  • static메서드는 인스턴스와 관련이 없다.
  • this와 마찬가지로 super 역시 static메서드에서는 사용할 수 없고 인스턴스 메서드에서만 사용이 가능하다.
class SuperTest{
	public static void main(String args[])
    {
    	Child c = new Child();
        c.method();
        
    }
}

class Parent{
	int x= 10;
}

class Child extends Parent{
	void method(){
    	System.out.println("x=" + x);
        System.out.println("this.x=" + this.x);
        System.out.println("super.x=" + super.x);
    }
}

실행결과
x= 10
this.x = 10
super.x = 10
Class SuperTest2{
	public static void main(String args[])
    {
    	Child c = new Child();
        c.method();
        
    }
}

class Parent{
	int x= 10;
}

class Child extends Parent{
	int x=20;

	void method(){
    	System.out.println("x=" + x);
        System.out.println("this.x=" + this.x);
        System.out.println("super.x=" + super.x);
	}
}

실행결과
x= 20
this.x = 20
super.x = 10
  • 이처럼 조상 클래스에 선언된 멤버변수와 같은 이름의 멤버변수를 자손 클래스중에서 중복해서 정의하는 것이 가능하며 참조변수 super를 이용해서 서로 구별할 수 있다.

  • 변수만이 아니라 메서드 역시 super를 써서 호출이 가능하다. 특히 조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에 super를 사용한다.

class Point{
	int x;
    int y;
    
    String getLocation(){
    	return "x : " + x + ",y :"+y;
    }
}

class Point3D extends Point{
	int z;
    String getLocation(){		//오버라이딩
    	//return "x : " + x + ",y :"+y +", z:"+ z;
        return super.getLocation() + ",z : " + z;	//조상메서드 호출
    }
}

getLocation()을 오버라이딩할 때 조상 클래스의 getLocation()을 호출하는 코드를 포함시켰다.

super() - 조상 클래스의 생성자

  • this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 조상 클래스의 생성자를 호출하는데 사용된다.

  • 생성자의 첫 줄에서 조상클래스의 생성자를 호출해야하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다.

Object클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super(),를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super();'를 생성자의 첫줄에 삽입한다.

예제)

class PointTest{
	public static void main (String args[])
    {
    	Point3D p3 = new Point3D(1, 2, 3);
    }
}
class Point{
	int x, y;
    
    Point(int x, int y)
    {
    	this.x = x;
        this.y = y;
    }

	String getLocation()
    {
    	return "x : "+ x + ", y :" + y;
    }
}

class Point3D extends Point
{
	int z;
    
    Point3D(int x, int y, int z)
    {
    /*
        생성자 첫 줄에서 다른 생성자를 호출하지 않기 때문에 컴파일러가
        'super();'를 여기에 삽입한다.
        super()는 Point3D의 조상인 Point클래스의 기본 생성자인 Point()를 의미한다.
    /*
    	this.x = x;
        this.y = y;
        this.z = z;
    }
    
    String getLocation(){
    	return "x : "+ x + ", y :" + y + ", z :" + z;
    }
}

이 예제를 컴파일하면 컴파일 에러가 발생한다.
Point3D클래스의 생성자에서 조상 클래스의 생성자인 Point()를 찾을 수 없다는 내용이다.
Point3D클래스의 생성자의 첫 줄이 생성자를 호출하는 문장이 아니기 때문에 컴파일러는 다음과 같이 자동적으로 'super();'를 Point3D클래스의 생성자의 첫 줄에 넣어 준다.

	Point3D(int x, int y, int z)
    {
    	super();
        this.x = x;
        this.y = y;
        this.z = z;
    }

Point클래스에 생성자 Point()가 정의되어 있지 않기 때문에 에러가 발생했던 것이다. 따라서 에러를 수정하려면 Point클래스에 생성자 Point()를 추가해주던가, 생성자 Point3D(int x, int y, int z)의 첫줄에서 Point(int x, int y)를 호출하도록 변경하면 된다.

	Point3D(int x, int y, int z)
    {
    	super(x, y);	// 조상클래스의 생성자 Point(int x, int y)를 호출
        this.z = z;
    }

위와 같이 변경하면 된다. 조상 클래스의 멤버변수는 이처럼 조상의 생성자에 의해 초기화되도록 해야 하는 것이다.

예제)

class PointTest2{
	public static void main (String args[])
    {
    	Point3D p3 = new Point3D();
        System.out.println("p3.x = " + p3.x);
        System.out.println("p3.y = " + p3.y);
        System.out.println("p3.z = " + p3.z);
    }
}

class Point{
	int x = 10;
    int y = 20;
    
    Point(int x, int y )
    {
    /*
        생정자 첫 줄에서 다른 생성자를 호출하지 않았기 때문에
        컴파일러가 'super();'를 여기에 삽입한다.
        super()는 Point의 조상인 Object 클래스의 기본 생성자인
        Object()를 의미한다.
    /*
    	this.x = x;
        this.y = y;
    }
}

class Point3D extends Point
{
	int z = 30;
    
    Point3d () 
    {
    	this(100, 200, 300);	//Point3D(nt x, int y, int z)를 호출
    }
    
    Point3D(int x, int y, int z)
    {
    	super(x, y);		//Point(int x, int y)를 호출
        this.z = z;
    }
}

실행결과
p3.x = 100
p3.y = 200
p3.z = 300

위 예제는 Point3D 클래스의 인스턴스를 생성할 때 어떤 순서로 인스턴스의 초기화가 진행되는지 보여준다.
Point클래스의 생성자 Point(int x, int y)는 어떠한 생성자도 호출하고 있지 않기 때문에 컴파일 후에 다음과 같은 코드로 변경된다.

	Point (int x, int y){
    	super();	//조상인 Object클래스의 생성자 Object()를 호출한다.
        this.x = x;
        this.y = y;
        
    }

어떤 클래스의 인스턴스를 생성하면, 클래스 상속관계의 최고조상인 Object클래스까지 거슬러 올라가면서 모든 조상클래스의 생성자가 순서대로 호출된다는 것을 알 수 있다.

profile
어제 보다는 내일을, 내일 보다는 오늘을 🚀

0개의 댓글