본문 바로가기
Java

220115 혼자공부하는자바 Ch7 상속

by 행인데어 2022. 1. 15.

07-1) 상속

클래스 상속

class 자식클래스 extends 부모클래스 {

  //필드

 //생성자

 //메소드

}의 형태로 작성한다.

자바에서 상속의 특징은 1)다중상속이 불가하고, 2)부모클래스에서 private 접근제한을 갖는 필드와 메소드는 상속대상에서 제외된다. 부모/자식클래스가 다른 패키지에 존재한다면 default접근제한을 갖는 필드와 메소드도 상속대상에서 제외된다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal3 extends Cal{
	//부모가 갖고 있지 않은 메소드를 추가
	public int minus(int v1, int v2) {
		return v1-v2;
	}                                    
	//부모가 갖고 있는 메소드를 재정의 ->Overriding
	public int sum(int v1, int v2) {
		System.out.println("Cal3!!");
		return v1+v2;
	}                                   
}

 

부모 생성자 호출

- this는 자기자신을 super는 부모클래스에 있는 메소드를 지칭한다.

- super(매개값1, 매개값2, ...)는 매개값의 타입과 일치하는 부모생성자를 호출한다. 일치하는 부모생성자 없으면 오류남

- super(매개값1, 매개값2, ...)가 생략되면 컴파일러에 의해 super()가 자동적으로 추가되므로 부모클래스의 기본생성자가 존재해야 한다. super(매개값1, ...)는 반드시 자식생성자 첫 줄에 위치해야 한다.

class Cal{
	int v1, v2;
	Cal(int v1, int v2) {
		System.out.println("Cal init!!"); 
		this.v1 = v1; this.v2 = v2;
	}
	public int sum() {return this.v1+v2;}
}

class Cal3 extends Cal{
	Cal3(int v1, int v2) {
		super(v1, v2);             //super는 부모 클래스의 생성자, 이거는 System.out.println("Cal init!!");와 같은 결과가 나옴
		System.out.println("Cal3 init!!");  //생성자가 있는 클래스를 상속받았다면 생성자를 만들어서 부모클래스를 반드시 호출해야한다.
	}
	public int minus() {return this.v1-v2;}
}
//메인메소드 실행
public class InheritanceApp1 {
	public static void main(String[] args) {
		Cal c = new Cal(2,1);
		Cal3 c3 = new Cal3(2,1);
		System.out.println(c3.sum());    //3
		System.out.println(c3.minus());  //1

	}
}

 

메소드 재정의(오버라이딩)

- 오버라이딩이란 자식클래스에서 부모클래스의 메소드를 다시 정의하는 것을 말한다.

- 오버로딩 규칙: 1)부모메소드와 동일한 타입, 이름, 매개변수 목록을 가져야 한다. 2)접근제한을 더 강하게 할 수 없다.

3) 새로운 예외를 throws 할 수 없다.

**주의: 오버로딩은 상속과는 관련이 없다. 같은 이름의 메소드 또는 생성자를 매개변수의 개수나 타입을 다르게 지정해서 2개이상 같은 이름으로 정의하는 것을 의미한다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	//같은 이름의 메소드를 정의 -> Overloading
	public int sum(int v1, int v2, int v3) {
		return v1+v2;
	}
}
class Cal3 extends Cal{
	//부모가 갖고 있지 않은 메소드를 추가
	public int minus(int v1, int v2) {
		return v1-v2;
	}                                    
	//부모가 갖고 있는 메소드를 재정의 ->Overriding
	public int sum(int v1, int v2) {
		System.out.println("Cal3!!");
		return v1+v2;
	}                                   
}
//메인메소드 실행
public class InheritanceApp1 {
	public static void main(String[] args) {
		Cal c = new Cal();
		System.out.println(c.sum(2,1));
		System.out.println(c.sum(2,1,1));    //입력값이 3개이면서 정수인 메소드를 찾아서 알아서 실행시켜줌
		
		Cal3 c3 = new Cal3();
		System.out.println(c3.sum(2,1));
		System.out.println(c3.minus(2,1));
		System.out.println(c3.sum(2,1));
	}
}

@Override 어노테이션을 써도되고, 안써도된다. 하지만 안전장치로 쓰는 것임. 그니까 걍 쓰자..

 

final클래스와 final메소드

- 클래스와 메소드를 선언할 때, final 키워드를 붙이면 상속과 관련있다는 의미이다.

- final클래스는 최종적인 클래스이므로 상속할 수 없다.

- 부모 클래스에 선언된 final메소드는 자식클래스에서 재정의할 수 없다.

 

** 상속에서 protected 접근제한자

protected접근제한자는 같은 패키지 내에서는 접근 제한이 없지만, 다른 패지키에서는 자식클래스에만 접근을 허용한다.

단 new 연산자를 사용해서 생성자를 직접 호출할 수는 없고, 자식생성자에서 super()로 호출할 수 있다.

 

 

07-2) 타입 변환과 다형성

다형성이란 객체 사용 방법은 동일하지만 실행결과가 다양하게 나오는 성질을 의미한다.

다형성을 구현하는 기술메소드재정의(오버라이딩)과 타입 변환이다. 예를 들면, 자식객체가 재정의된 메소드를 가지고 있을때 부모 타입으로 자동 타입 변환 후에 메소드를 호출하면 재정의된 자식메소드가 호출되면서 다양한 실행결과를 가져올 수 있다.

 

자동 타입 변환

클래스의 변환은 상속 관계에 있는 관계에서 발생한다. 자식은 부모 타입으로 자동 타입 변환이 가능하다.

바로 위 부모가 아니라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있다.

//부모클래스
class Animal {
  ...
}

//자식클래스
class Cat extends Animal {
  ...
}

//Cat클래스로부터 Cat객체를 생성하고 이것을 Animal변수에 대입하면 자동 타입 변환이 일어난다.
Cat cat = new Cat();
Animal animal = cat;   // Animal animal = new Cat();도 가능

이때, animal변수와 cat변수는 타입은 다르지만 동일한 Cat객체를 참조한다.

즉 cat == animal은 true가 된다.

 

부모 타입으로 자동 타입 변환된 후에는 부모크래스에 선언된 필드와 메소드에만 접근이 가능하다. (자식클래스는 불가능) 그러나 예외가 있는데, 메소드가 자식클래스에서 재정의(오버라이딩)되었다면 자식클래스 메소드가 대신 호출된다.

//부모클래스
class Parent {
	void method1() {}
    void method2() {}
}

//자식클래스
class Child extends Parent {
	void method2() {}      //오버라이딩!!
    void method3() {}
}

//메인메소드가 있는 클래스
class ChildExample {
	public static void main(String[] args) {
    	Child child = new Child();   //Child클래스에서 객체 생성
        
        Parent parent = child;      //child변수 타입이 부모 타입으로 자동 변환된다.
        
        parent.mathod1();     //부모클래스에서 호출
        parent.mathod2();     //자식클래스에서 오버라이딩된 메소드는 타입 변환 후에도 자식메소드 호출
        parent.mathod3();     //부모 타입으로 변환했으니까 자식클래스의 메소드는 호출 불가능
}