2020년 10월 20일
업데이트:
상속(Inherit)
다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능이다.
상속의 목적
클래스의 재사용, 연관된 일련의 클래스들에 대한 공통적인 규약을 정의한다.
상속의 장점
- 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있다. -> 중복 제거 / 재사용성 증가
- 코드를 공통적으로 관리하기 때문에 코드의 추가 및 변경이 용이하다. -> 유지보수 / 생산성 증가
상속의 특징
- 모든 클래스는 Object클래스의 후손
- Object클래스가 제공하는 메소드를 오버라이딩하여 메소드 재구현이 가능하다.
- 부모 클래스의 생성자, 초기화 블록은 상속 불가
- 자식 클래스 생성 시, 부모 클래스 생성자가 먼저 실행됨
- 자식 클래스 생성자 안에서 부모 클래스 생성자 호출을 명시하고 싶으면 super() 활용
- 부모의 private 멤버는 상속이 되긴 하지만 직접 접근은 불가
- 자식 객체 생성 시에 부모의 필드 값도 전달 받은 경우, 자식 생섣자 안에서 부모의 private 필드에 직접 접근하여 대입할 수 없다. super()를 이용하여 전달받은 부모의 필드 값을 부모 생성자 쪽으로 넘겨서 생성하거나 setter, getter 메소드를 이용하여 접근해야한다.
상속 사용 방법
클래스 간의 상속 시에는 extends 키워드를 사용한다.
[접근제한자] class 클래스명 extends 클래스명 {}
public class Academy extends Company {}
단일 상속과 다중 상속
단일 상속
클래스 간의 관계가 다중 상속보다 명확
하고 신뢰성
있는 코드 작성. 자바에서는 단일상속만 지원한다.
다중 상속
C++에서 가능한 기능으로 여러 클래스로부터 상속을 받으며 복합적인 기능을 가진 클래스를 쉽게 작성 가능함. 서로 다른 클래스로부터 상속 받은 멤버 간의 이름이 같은 경우 문제가 발생한다.
super(), super.
super()
부모 객체의 생성자를 호출하는 메소드로 기본적으로 자식 생성자에 부모 생성자가 포함된다. 객체 생성 시 부모부터 생성이 되기 때문에 자신 클래스 생성자 안에는 부모 생성자를 호출하는 super()
가 첫 줄에 존재한다.
super.
상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수. 자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용한다.
오버라이딩(Overriding)
자식
클래스가 상속 받은 부모 메소드를 재작성
하는 것이며 부모가 제공하는 기능을 후손이 일부 고쳐 사용하겠다는 의미로, 자식 객체를 통한 실행 시 후손 것이 우선권을 가진다.
특징
메소드 헤드라인 위에 반드시 Annotation, @Overrid 표시
접근 제어자를 부모 것보다 같거나 넓은 범위로 변경이 가능하다.
부모 메소드의 예외처리 클래스 처리 범위보다 좁은 범위로 예외처리 클래스 수정이 가능하다.
성립 조건
- 메소드 이름 동일
- 매개 변수의 개수, 타입 동일
- 리턴 타입 동일
- private 메소드 오버라이딩 불가
오버로딩(Overloading)
한 클래스 내에서 같은 이름의 메소드를 여러 개 정의하는 것이다.
성립 조건
같은 메소드 이름, 다른 매개변수 선언부(매개변수 타입, 개수 순서)
주의 사항
메소드 리턴타입은 오버로딩 조건과는 관계가 없다.
오버라이딩(Overriding) | 오버로딩(Overloading) | ||||
하위 클래스에서 메소드 정의 | 같은 클래스에서 메소드 정의 | ||||
메소드 이름 동일, 매개변수 동일(개수, 타입), 리턴 타입 동일 | 메소드 이름 동일, 매개변수 다름(개수, 타입) 리턴 타입 상관 없음 | ||||
자식 메소드의 접근 범위가 부모 메소드의 접근 범위보다 넓거나 같아야 함 | 접근 제어자와 상관없음 | ||||
자식 메소드의 예외 수가 부모 메소드의 예외 수보다 적거나 범위가 좁아야 함 | 예외처리와 상관 없음 |
final 클래스
// 상속이 불가능한 클래스
public final class FinalClass {}
//상속 시 오버라이딩이 불가능한 패소트
public final void method() {}
}
//Parent.java
public class Parent {
public int assets = 1000000000;
private int goldBar = 3;
public int getGoldBar() {
return goldBar;
}
public void setGoldBar(int goldBar){
this.goldBar = goldBar;
}
public void hun_yug() {
System.out.println("공부좀 해라!!!!!");
}
}
//Child1.java
public class Child1 extends Parent {
// extends 예약어
// - 자식 클래스가 부모 클래스를 상속하는 경우 필드, 메소드의 양이 증가하므로
// 확장한다는 의미의 예약어를 사용함
private String myCar = "마세라티";
private String myHouse = "반지하";
public String getMyCar() {
return myCar;
}
public void setMeyCar(String myCar){
this.myCar = myCar;
}
public String getHouse() {
return myHouse;
}
public void serMyHouse(String myHouse) {
this.myHouse = myHouse;
}
}
//Child2.java
public class Child2 extends Parent {
private String computer = "150만원짜리 컴퓨터";
private String membership = "천생연분";
public void setComputer(String computer) {
this.computer = computer;
}
public void setMembership(String membership) {
this.membership = membership;
}
public String getComputer() {
return computer;
}
public String getMembership() {
return membership;
}
}
//InheritService.java
public class InheritService {
public void example1() {
Child1 c1 = new Child1();
Child1 c2 = new Child1();
c1.assets -= 100000000;
c2.assets += 2500000;
System.out.println("C1의 자산 : " + c1.assets);
System.out.println("C2의 자산 : " + c2.assets);
// 같은 부모를 둔 상속 받은 객체들은 부모의 멤버 변수 값을
//공유하는 것이 아닌 각각 부모 멤버 변수 값을 복사하는 것이다.
// 부모의 private 필드 또는 메소드는 상속이 되지만 자식이 직접 접근 불가
//c1.goldBar
// -> 부모 객체에 정의된 private 필드 접근 가능한 메소드를
// 사용하면 된다.
}
}
//Calauator.java
public class Calculator extends Object{
// -> 생략 될 경우 컴파일러가 object 상속 구문을 자동 추가
// ~ (defulat) : 같은 패키지 내부에서 접근 가능
// # protected : 같은 패키지 + 다른 패키지 중 상속 관계인 클래스에서 접근 가능
final int MIN_INT = -2147483648;
protected int MAX_INT = 2147483647;
private double saveNum1;
private double saveNum2;
// 기본 생성자
public Calculator() {
// 자식 클래스의 생성자를 이용해서 자식 객체 생성 시 내부에 부모 객체가 생성이 되어야 함
// -> 부모 생성자 호출 == super() 생성자
//super(); // 자식 객체 내부에 부모 객체를 생성해주는 생성자
// *** 생성자 코드 컴파일 시
// super() 생성자가 누락되어 있다면
// 생성자 내부 *첫 번째 줄*에 super(); 생성자 코드를 자동 추가함
// -> super(), this() 무조건 생성자 첫 번째 줄에 작성해야 함
}
// 매개 변수 있는 생성자
public Calculator(double saveNum1, double saveNum2) {
this.saveNum1 = saveNum1;
this.saveNum2 = saveNum2;
}
public double getSaveNum1() {
return saveNum1;
}
public void setSaveNum1(double saveNum1) {
this.saveNum1 = saveNum1;
}
public double getSaveNum2() {
return saveNum2;
}
public void setSaveNum2(double saveNum2) {
this.saveNum2 = saveNum2;
}
// 일반 산술 연산
public String calculate(double num1, double num2, char op) {
String result = null;
if(num2 == 0 && (op == '/' || op == '%')) {
result = "0으로 나눌 수 없습니다.";
} else {
switch (op) {
case '+': result = num1 + " + " + num2 + " = " + (num1 + num2); break;
case '-': result = num1 + " - " + num2 + " = " + (num1 - num2); break;
case '*': result = num1 + " * " + num2 + " = " + (num1 * num2); break;
case '/': result = num1 + " / " + num2 + " = " + (num1 / num2); break;
case '%': result = num1 + " % " + num2 + " = " + (num1 % num2); break;
default: result = op + "연산자는 존재하지 않습니다.";
}
}
return result;
}
// 제곱 연산
public double squared(double num) {
return num * num;
}
// 팩토리얼 연산
// 1부터 num까지 모든 정수의 곱
// 5! = 1 * 2 * 3 * 4 * 5 = 120
public int factorial(int num) {
int result = 1;
for(int i = 1; i <= num; i++) {
result *= i;
}
return result;
}
//object toString() 오버라이딩
// 오버라이딩 : 상속받은 부모 메소드를 자식이 재정의 하는 것
// ********** 오버라이딩 작성 조건!!
// 1) 메소드명이 동일해야 함
// 2) 반환형 동일
// 3) 매개 변수 순서, 개수, 타입 모두 동일
// 4) 접근제한자는 범위가 같거나 더 넓은 범위로 변경 가능
// 5) (참고) 예외처리의 범위도 같거나 더 구체적인 범위로 변경
@Override // @Override Annotation : 컴파일러에게 오버라이딩 메소드임을 명시하는 역할
public String toString() {
// toString() 메소드 용도 : 객체의 모든 필드값 반환
return saveNum1 + " / " + saveNum2;
}
}
//CalculatorService
public class CalculatorService {
public void example() {
// Calculator 클래스와 같은 패키지
Calculator cal = new Calculator();
System.out.println(cal.MIN_INT); // (default)
System.out.println(cal.MAX_INT); // protected
cal.setSaveNum1(100);
cal.setSaveNum2(3.12345);
System.out.println(cal.toString());
RectangleCalculator rc = new RectangleCalculator(3.14, 2.51, 99.99, 123456);
System.out.println(rc.toString());
System.out.println(rc);
// print 관련 구문 내에서 참조 변수명을 작성한 경우
// 참조 중인 객체의 toString()을 호출할 수 있도록
// 컴파일러가 .toString()을 자동으로 추가해줌
}
// 둘레
public double perimeter(double num1, double num2) {
return (num1 + num2) * 2;
}
// 넓이
public double area(double num1, double num2) {
return num1 * num2;
}
}
//InheritService.java
public class InheritService {
public void example1() {
Child1 c1 = new Child1();
Child2 c2 = new Child2();
// c1, c2가 부모 객체인 Parent의 멤버 변수를 사용할 수 있음
c1.assets -= 100000000; // 첫째가 1억 씀
c2.assets += 2500000; // 250만원 추가
// 같은 부모를 상속받은 객체들은
// 부모의 멤버 변수 값을 공유하는 것이 아닌
// 각각 부모 멤버 변수 값을 복사하는 것이다.
System.out.println("c1의 자산 : " + c1.assets);
System.out.println("c2의 자산 : " + c2.assets);
// 부모의 private 필드 또는 메소드는 상속은 되지만
// 자식이 직접 접근할 수 없음!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//c1.goldBar
// -> 부모 객체에 정의된 private 필드 접근 가능한 메소드를 사용하면 된다.
System.out.println("물려받은 골드바 개수 : " + c1.getGoldBar());
System.out.println("물려받은 골드바 개수 : " + c2.getGoldBar());
// 자식만의 고유한 필드 또는 메소드도 사용 가능합.
System.out.println("c1의 자동차 : " + c1.getMyCar());
System.out.println("c2의 배민 등급 : " + c2.getMembership());
}
}
//RactanlgeCalculator.java
public class RectangleCalculator extends Calculator {
// 자식만의 필드
private double saveNum3;
private double saveNum4;
public RectangleCalculator() { } // 기본생성자 단축키 컨트롤 + 스페이스바 + 엔터
// 매개변수 있는 생성자
public RectangleCalculator(double saveNum1, double saveNum2, double saveNum3, double saveNum4) {
// 객체 생성 시 한 번에 모든 필드를 초기화하고자 하는 경우
// 부모로부터 상속받은 saveNum1, saveNum2도 초기화를 진행해야 함
// 부모 필드 초기화
// 방법 1) setter 사용
// setSaveNum1(saveNum1);
// setSaveNum2(saveNum2);
//방법 2) 부모 생성자 사용 --> super() 생성자 사용
super(saveNum1, saveNum2);
// 자식 필드 초기화
this.saveNum3 = saveNum3;
this.saveNum4 = saveNum4;
}
public void example() {
System.out.println(MAX_INT); // protected
// 다른 패키지이지만 상속관계(자식)를 맺고 있으므로 protected 접근제한자가 붙은 MAX_INT를 직접 접근할 수 있음
//System.out.println(MIN_INT); // (default)
// default 접근제한자는
// 같은 패키지 내에서만 접근 가능하므로
// 상속 관계에 있어도 다른 패키지일 경우 직접 접근 불가
}
//Calculator가 재정의한 toString() 메소드를 또 재정의
@Override
public String toString() {
// super 참조변수
// 상속 관계를 가진 자식 객체 생성 시
// 내부에 존재하는 부모 객체의 시작 주소를 참조하는 참조 변수
// (비슷한 역할을 하는 변수 : this 참조 변수)
// * 컴파일러에 의해 자동 추가됨
return super.toString() + " / " + saveNum3 + " / " + saveNum4;
}
}
다형성
객체지향 프로그래밍 3대 특징 중 하나로 ‘여러 개의 형태를 갖는다’는 의미이다. 하나의 행동으로 여러 가지 일을 수행하는 개념이며, 상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는여러 타입의 자식 객체를 참조할 수 있는 기술이다.
업 캐스팅(Up Casting)
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입 객체의 주소를 참조할 수 있다.
댓글남기기