국비지원 복습과정 7차(super 키워드)

Park In Kwon·2022년 9월 6일
0

1. super

1-1. super 키워드

  • 클래스의 상속관계에서 자식 클래스가 부모 클래스를 가리키는 예약어

1-2. 사용방법

  • 멤버변수 이름 앞에 명시
    -> 부모 클래스의 멤버변수를 의미한다. 하지만 부모클래스의 멤버변수는
    이미 모두 상속되어 있기 때문에 이 경우에는 this 키워드를 사용하는
    것과 동일한 결과이기에 잘 사용하지 않는다.

  • 메서드 이름 앞에 명시
    -> 부모 클래스의 메서드를 의미한다.
    -> 재정의 되지 않은 메서드 : 이미 상속되어 있기 때문에 this 키워드를
    사용하는 것과 동일한 결과를 갖는다.
    -> 재정의된 메서드 : Override 된 메서드 이름 앞에 사용하게 되면
    재정의 되기 이전의 원본 메서드를 의미한다.

  • 키워드 자체를 메서드처럼 사용
    -> 부모 클래스의 생성자를 의미한다.

1-3. super 활용

  • super(2/4) 설명
    -> Hello 클래스가 Korean에 상속되고, Korean클래스가 say() 메서드를
    Override 처리하게 되면 Korean 클래스의 객체는 더 이상 부모의
    say() 메서드에 접근할 수 없게 된다.

  • super(3/4) 설명
    -> 만약 부모 클래스가 가지고 있는 say()메서드에 추가적인 기능을
    구현하고자 한다면 부모의 코드를 그대로 복사해서 사용해야 할 것이다
    원본 기능에 대한 수정이 발생할 경우 부모 클래스와 자식 클래스를
    모두 수정해야 하기 때문에 소스코드의 유지보수 효율성이
    떨어지게 된다.

  • super(4/4) 설명
    -> super 키워드는 부모 클래스의 메서드를 호출하는 기능을 가지고
    있기 때문에, Override된 자식 클래스의 메서드에서 super 키워드를
    사용하면, 재정의 되기 이전의 부모 클래스에 대한 메서드를
    호출할 수 있다.

1-4. 상속관계에서의 생성자 처리

  • 생성자는 상속되지 않는다.
  • 하지만 생성자가 정의된 클래스는 객체 생성을 위해 생성자 파라미터를반드시 전달 받아야 하기
    때문에, 파라미터를 갖는 생성자가 정의된클래스를 상속받게 되면 에러가 발생한다.

1-5. 부모 생성자의 강제 호출

  • 생성자가 정의된 클래스를 상속받은 경우에는 자식 클래스의 생성자를
    통해서 부모 생성자를 강제로 호출해야 한다.
  • 부모의 생성자를 호출하는 방법은 super키워드를 메서드 이름으로 사용하는 것이다.

1-6. 메서드 재정의 과정에서 오타가 발생한 경우

  • 부모 클래스의 'say()'메서드를 재정의 하는 과정에서 개발자의 실수로
    'sai()' 라고 메서드가 추가되었다면, Java는 새로운 메서드가 추가한
    것으로 인식하고 특별한 에러를 표시하지 않는다.

1-7. 메서드 재정의 과정에서의 오타 방지 옵션

  • "@Override"는 이 키워드가 명시된 위치 아래에 정의되는 메서드가
    부모 클래스에 존재하지 않을 경우 구문 에러로 처리한다.
  • 부모 클래스의 메서드를 재정의 하고자 할 경우, 의도치 않은 실수를
    예방하기 위한 '오타 방지용 옵션' 이다.
  • 여러 개의 메서드를 재정의 한다면 재정의 되는 모든 메서드들 위에 각기
    명시해 한다.
package casting;

public class AirForce extends Unit {
	
	public AirForce (String name) {
		super (name);
	}
	
	public void attack() {
		super.attack();
		System.out.println(this.getName() + ">> 이륙"); 
		System.out.println(this.getName() + ">> 공중공격 실행"); 
	}
	
	public void bombing() {
		System.out.println(this.getName() + ">> 폭격");
	}
}

package casting;

public class Army extends Unit {

	public Army(String name) {
		super(name);
	}
	// Override 메서드
	// 공격의 형태를 육군에 맞게 변경하지만, 부모 클래스가 가지고 있는 공격 준비
	// 기능을 super 키워드를 재사용해서 호출하려고함
	@Override
	public void attack() {
		super.attack();
		System.out.println(super.getName() + ">> 지상공격 실행함");
	}
	
	public void tank() {
		System.out.println(super.getName() + " >>탱크 공격");
	}
}

package casting;

public class Navy extends Unit {
	
	public Navy (String name) {
		super(name);
	}
	
	public void attack() {
		super.attack();
		System.out.println(this.getName() + ">> 해상공격 실행함"); 
		System.out.println(this.getName() + ">> 지상 상륙"); 
		
	}
	
	public void nucleus() {
		System.out.println(this.getName() + ">> 핵미사일");
		
	}
}

package casting;

public class Unit {
	private String name;
	
	public Unit(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public void attack() {
		System.out.println(this.name + ">>> 공격준비");
	}
 }
 
 package casting;

public class Main02 {

	public static void main(String[] args) {
		AirForce af = new AirForce("공군");
		Navy nv = new Navy("해군");
		Army am = new Army("육군");
		
		// 각각의 객체는 자신들의 고유 기능을 사용할 수 있다.
		af.bombing();
		nv.nucleus();
		am.tank();
		
		System.out.println("-------------------");
		
		// 암묵적 형변환
		Unit temp1 = af;
		Unit temp2 = nv; 
		Unit temp3 = am;
		
		//형변환이 되더라도 상속받거나 재정의한(Override) 자신들의 기본 특성은 그대로 유지함
		temp1.attack();
		temp2.attack();
		temp3.attack();
		
		// 상위 클래스 형태로 형변환이 되면, 자신들의 독립 기능은 사용하지 못함
//		temp1.bombing();
//		temp2.nucleus();     
//		temp3.tank();
		
		System.out.println("-------------------------");
		// 원래의 기능을 되돌리기 위해서는 하위 클래스 형태로 명시적 형변환이 필요
		AirForce re1 = (AirForce)temp1;
		Navy re2 = (Navy)temp2;
		Army re3 = (Army)temp3;
		
		re1.bombing();
		re2.nucleus();
		re3.tank();
	}
}

2. 오버로드

2-1. 메서드 오버로드(Overload)

  • 원칙적으로 하나의 클래스 안에는 동일한 이름의 메서드가 두개 이상
    존재할 수 없지만, 이를 가능하게 하는 예외적인 처리 기법

2-2. 이름이 동일한 메서드를 정의하기 위한 조건

  • 메서드간의 파라미터가 서로 달라야한다.
    -> 파라미터의 데이터 타입이 다르다.
    ( 데이터 형이 동일하고 변수의 이름이 다른경우는 동일한 파라미터로 인식된다)
    -> 파라미터의 개수가 다르다.
    -> 서로 다른 데이터형을 갖는 파라미터들의 전달 순서가 다르다.

  • 리터형이 다른 경우는 오버로드의 성립에 아무런 영향을 주지 않는다.

2-3. 오버로드 예시

  • 파라미터의 데이터형이 서로 다르기 때문에 오버로드 성립
    public void user(int a){ .. }
    public void user(long a){ .. }

  • 파라미터의 개수가 서로 다르기 때문에 오버로드 성립
    public void user( int a ){..}
    public void user( int a, int b ){..}

  • 데이터 형의 전달 순서가 서로 다르기 때문에 오버로드 성립
    public void user(int a, String b){..}
    public void user(String b, int a ){..}

  • 오버로드 성립 불가
    public void user(int a, String b){...}
    public void user(int b, String a){...}

2-4. 생성자의 Overload

  • 객체 생성 방법의 다양화
  • 생성자 역시 메서드의 한 종류이므로 Overload가 가능하다
  • 생성자를 Overload 할 경우, 해당 클래스에 대해 '객체를 생성하는 방법'을
    다양하게 준비할 수 있게 된다.

2-5. this 키워드를 사용한 생성자 Overload

  • this 키워드의 용법
    -> 메서드처럼 사용할 경우, 현재 클래스의 다른 생성자를 의미한다.
  • this 키워드를 사용하여 생성자 Overload를 간결하게 처리하기
    -> 파라미터가 서로 다른 생성자들이 하나의 완전한 생성자를 호출하도록 하여,
    데이터의 초기화를 한 곳에서 일괄적으로 처리하도록 구현할 수 있다.
package override;

public class AirForce extends Unit {
	
	public AirForce (String name) {
		super (name);
	}
	
	public void attack() {
		super.attack();
		System.out.println(this.getName() + ">> 이륙"); 
		System.out.println(this.getName() + ">> 공중공격 실행"); 
	}
	
	public void bombing() {
		System.out.println(this.getName() + ">> 폭격");
	}
}

package override;

public class Army extends Unit {

	public Army(String name) {
		super(name);
	}
	// Override 메서드
	// 공격의 형태를 육군에 맞게 변경하지만, 부모 클래스가 가지고 있는 공격 준비
	// 기능을 super 키워드를 재사용해서 호출하려고함
	@Override
	public void attack() {
		super.attack();
		System.out.println(super.getName() + ">> 지상공격 실행함");
	}
	
	public void tank() {
		System.out.println(super.getName() + " >>탱크 공격");
	}
}

package override;

public class Navy extends Unit {
	
	public Navy (String name) {
		super(name);
	}
	
	public void attack() {
		super.attack();
		System.out.println(this.getName() + ">> 해상공격 실행함"); 
		System.out.println(this.getName() + ">> 지상 상륙"); 
		
	}
	
	public void nucleus() {
		System.out.println(this.getName() + ">> 핵미사일");
		
	}
}

package override;

public class Unit {
	private String name;
	
	public Unit(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public void attack() {
		System.out.println(this.name + ">>> 공격준비");
	}
}

package override;

class Hello{
	public void say() {
		System.out.println("Hello");
	}
}

class Korean extends Hello{
	//korean 클래스에 의해 say() 메서드가 Override된 경우
	public void say () {
		System.out.println("안녕하세요");
	}
	
	public void sayHello() {
		super.say();  // 부모 클래스(Hello)의 say()
		this.say();   // 나 자신(Korean)의 say()
	}
}

public class Main01 {

	public static void main(String[] args) {
		Korean kor = new Korean();
		kor.sayHello();
		System.out.println("------------------------");
		kor.say();
	}
}

package override;

public class Main03 {

	public static void main(String[] args) {
		Army am = new Army("육군");
		Navy nv = new Navy("해군");
		AirForce af = new AirForce("공군");
		
		am.attack();
		am.tank();
		
		nv.attack();
		nv.nucleus();
		
		af.attack();
		af.bombing();

	}
}

3. 객체 형변환과 객체 배열

3-1. 객체 형변환

  • java 기본 유형의 데이터들처럼 객체 참조변수의 경우에도 형변환(casting)이
    이루어 진다.

  • 서로 다른 클래스 유형으로부터 나온 객체 참조변수들 간의 대입에는
    일정한 규칙이 있다.

    	Parent parent = new Child();
  • 왼쪽 항(부모 클래스)과 오른쪽 항(자식 클래스)의 객체 유형이 서로 다른 경우,
    두 유형이 서로 상속 관계에 있고 왼쪽 객체(부모 클래스)가 오른쪽 객체(자식클래스)
    의 상위 클래스인 경우에만 암묵적 형변환이 일어난다.

  • 하위 클래스에서 상위클래스 유형으로 항당하는 것은 가능하나 그 반대의 경우에는
    명시적 형변환을 해야한다.
    -> 그러나 상위 클래스 유형을 하위 클래스 유형으로 강제 형 변환하는
    경우에는 할당되는 객체의 유형에 따라서 실행 오류가 발생할 수 있다.

3-2. 객체간의 암묵적 형변화

A a1 = new B();
A a2 = new X();

A a3 = new C();
A a4 = new Y();

B b1 = new C();
X x1 = new Y();

C c = new C();
B b2 = c;

Y y = new Y();
X x2 = y;

3-3. 암묵적 형변환과 메서드 오버라이드

  • 암묵적 형변환은 부모를 상속받는 자식객체의 기능을 부모에게
    물려받은기능만 사용하도록 제한한다.

  • 그러므로 암묵적 형변환이 발생하게 되면 오버라이드된 기능만 사용가능
    하고, 추가적으로 구현한 기능은 사용할 수 없다.

  • 주의할 점은 기능의 제한이지 기능의 변경은 아니다.

    Unit u1 = new Army();
    Unit u2 = new Navy();
    Unit u3 = new AirForce();

    u1.attack();
    u2.attack();
    u3.attack();

  • 상속관계의 객체를 부모형태로 변환하면 클래스의 종류를 구분하지 않고
    일관된 기능을 호출할 수 있다.

  • 객체가 상위클래스형태로 형변환 되더라도 Override된 자신의 기능은
    잃지 않는다.

  • 하지만, 추가적으로 구현했던 기능은 사용할 수 없게 되므로
    원래의 기능을 다시 사용할 수 있는 방법이 필요해졌다.

3-4. 명시적 형변환

  • 부모 클래스의 객체를 자식 클래스 형태로 변환하는 것

  • 형변환을 위해서는 변환할 클래스 이름을 명시적으로 지정해 주어야 한다.

    ChildClass child = (ChildClass)parent;

3-5. 명시적 형변환의 조건

  • 객체가 최초 생성될 때 자식 클래스 형태로 생성되고,
    부모 형태로 암묵적 형변환이 된 상태를 다시 원래 자식 클래스 형태로
    되돌릴 경우에만 가능하다.

ChildClass child1 = new ChildClass();
ParentClass parent = child1; // 암묵적 형변환
ChildClass child2 = (ChildClass)parent;

3-6. 명시적 형변환이 가능한 경우

Army army1 = new Army();
Unit u = army1;
Army army2 = (Army)u;

 --------------

Unit u = new Navy();
Navy navy = (Navy)u;

3-7. 불가능한 경우

  • 최초 객체 생성이 부모 형태로 만들어진 경우 불가능하다.
    Unit u = new Unit();
    Army army = (Army)u;
    	-------------------------------
  • 최초 생성된 것과 다른 형식으로 변환하는 것은 불가능 하다.
    Army army = new Army();
    Unit u = army;
    Navy navy = (Navy)u;

=> 위의 두 경우 모두 문법적인 오류는 없기 때문에,
이클립스에서는 에러를 검출하지 못한다. 하지만 프로그램을 실행
시켰을 경우에는 에러가 발생한다.

profile
개발자로 진로 변경을 위해 준비하고 있습니다

0개의 댓글