[JAVA] 상속

MINJEE·2024년 1월 11일

SMHRD_8_Java

목록 보기
6/10
post-thumbnail

상속 (inheritance)

🔷 클래스 상속

class 서브클래스 extends 슈퍼클래스 { ... }
  • 부모(슈퍼) 클래스를 물려받아 자식(서브)클래스로 확장
  • 서브클래스에서는 슈퍼클래스에서 선언된 필드와 메소드를 선언할 필요 없음
  • 다중 상속불허 (인터페이스의 다중 상속 허용)
    -> extends 키워드 뒤에는 하나의 슈퍼클래스만 올 수 있음!
  • 모든 클래스는 묵시적으로 Object클래스를 상속받음

🔷 생성자 호출

  • 서브 클래스 객체 생성 시, 슈퍼 클래스 생성자와 서브 클래스 생성자 모두 실행
  • 순서 : 서브 클래스 생성자 호출 → 슈퍼 클래스 생성자 호출 → 슈퍼 클래스 생성자 실행 → 서브 클래스 생성자 실행
  • 슈퍼 클래스의 생성자가 결정되는 방식
    • 명시적 : super(파라미터) 이용
      (반드시 서브클래스 생성자 코드의 첫 라인에 작성)
    • 묵시적 : 컴파일러가 자동으로 기본생성자 선택
      (슈퍼 클래스에 기본생성자는 없고 매개변수 있는 생성자 있으면 오류발생
      → 슈퍼클래스에 기본생성자를 따로 생성해야 함)
// 묵시적 슈퍼클래스 생성자 선택
class A{
		public A(){ ... } //기본생성자
		public A(int x) { ... } //생성자
}
class B extends A {
		public B() { ... }
		public B(int x) { ... } //슈퍼클래스의 기본생성자 호출
}
public class Example {
		public static void main(String[] args) {
				B b;
				b = new B(5); // B(int x)생성자 호출 -> A()기본생성자 호출
		}
}
// 명시적 슈퍼클래스 생성자 선택
class A{
		public A(){ ... }
		public A(int x) { ... }
}
class B extends A {
		public B() { ... }
		public B(int x) { 
				super(x);
				...
		}
}
public class Example {
		public static void main(String[] args) {
				B b;
				b = new B(5); // B(int x)생성자 호출 -> A(int x)생성자 호출
		}
}

🔷 업캐스팅 (Upcasting)

: 서브클래스의 레퍼런스를 슈퍼클래스 레퍼런스에 대입하는 것
  • 슈퍼클래스명 클래스변수 = new 서브클래스명(); : 업캐스팅
  • 업캐스팅된 레퍼런스 변수는 서브클래스 객체의 멤버 중에서 상속받은 슈퍼클래스의 멤버만 접근 가능
  • instanceof 연산자 : 레퍼런스가 가리키는 객체의 타입 식별 (true/false로 리턴)
    • 클래스변수 instanceof 클래스명
      (클래스변수는 객체레퍼런스, 클래스명은 객체타입을 의미)
class A {
		int a1; //A의 멤버필드
		public int a2(){ ... } //A의 멤버메소드
}
class B extends A {
		int b1; //A의 멤버필드
		public int b2() { ... } //B의 멤버메소드
}

A a = new B(); //업캐스팅
a.a1 =;
a.a2();
//a.b1 = 값; //컴파일 오류
//a.b2(); //컴파일 오류

a instanceof A //true
a instanceof B //true => new B() 객체는 A타입이면서, 동시에 B타입

🔷 다운캐스팅

: 슈퍼클래스 레퍼런스를 서브클래스 레퍼런스에 대입
  • 서브클래스명 클래스변수 = (서브클래스명)업캐스팅된변수; : 다운캐스팅
  • 업캐스팅된 것을 다시 원래대로 되돌리는 것
  • 반드시 명시적 타입 변환으로 지정
  • 상속받은 슈퍼클래스의 멤버뿐만 아니라, 서브클래스의 멤버도 접근 가능해짐
A a = new B(); //업캐스팅
B b = (B)a; //다운캐스팅(강제 타입 변환)
b.a1 =;
b.a2();
b.b1 =; //오류 발생x
b.b2(); //오류 발생x

🔷 메소드 오버라이딩

: 슈퍼클래스 메소드의 원형(메소드명, 인자 타입, 인자 개수, 리턴 타입)과 동일하게 서브클래스에서 메소드를 재정의(중복 작성)하는 것
  • 슈퍼클래스의 메소드 무력화 → 서브클래스의 오버라이딩한 메소드가 실행
  • 목적 : 다형성 실현(슈퍼클래스 메소드를 서브클래스에서 각각 목적에 맞게 다르게 구현 가능)
class A {
		void f() { Sys.out.println("A의 f()"); } //A의 멤버메소드
}
class B extends A {
		void f() { Sys.out.println("B의 f()"); } //B의 멤버메소드(A의 f()를 오버라이딩)
}

A v1 = new A();
v1.f(); //A의 f()
B v2 = new B();
v2.f(); //B의 f()
A v3 = new B();
v3.f(); //B의 f() => A의 f()를 호출해도, 동적바인딩되어 오버라이딩 된 메소드인 B의 f()가 실행
  • super.슈퍼클래스의멤버 : 슈퍼클래스의 멤버에 접근
    • 서브 클래스에서만 사용
    • super로 슈퍼클래스의 메소드 호출 = 정적 바인딩
class A {
	protected String a1;
	public void f1() {
		f2();
	}
	public void f2() {
		System.out.println(a1);
	}
}

public class B extends A {
	protected String a1;
	public void f2() {
		a1 = "Sub";
		super.a1 = "Super";
		super.f2();
		System.out.println(a1);
	}

	public static void main(String [] args) {
		A v = new B(); //업캐스팅
		v.f1(); //클래스A의 f1()메소드 실행
						// -> 오버라이딩으로 f2()함수는 클래스B의 f2()메소드 실행(동적바인딩)
						// -> 클래스B의 a1필드에 "Sub"대입, 클래스A의 a1필드에 "Super"대입
						// -> 클래스A의 f2()메소드 실행(정적바인딩)하여 클래스A의 a1필드 출력
						// -> 클래스B의 a1필드 출력
		//결과 : Super \n Sub
	}
}
메소드 오버로딩메소드 오버라이딩
선언동일 클래스 또는 상속관계에서 동일한 메소드명 중복 작성서브클래스에서 슈퍼클래스에 있는 메소드와 동일한 메소드명 재작성
목적사용의 편리성 향상서브클래스에서 새로운 기능의 메소드 재정의
조건메소드명 동일, 인자 개수/타입 달라야함메소드명, 인자 타입/개수, 리턴타입 등 모두 동일!
바인딩정적 바인딩(컴파일 시 중복된 메소드 중 호출메소드 결정)동적 바인딩(실행 시간에 오버라이딩된 메소드 찾아 호출)

🔷 추상 클래스/메소드

  • 추상 메소드 : abstract로 선언된 메소드 (메소드 실현부는 없고, 원형만 선언)
  • 추상 클래스 : abstract로 선언된 클래스
    • 추상메소드 포함할수도 안할수도 있음
    • 추상메소드를 가지는 클래스는 반드시 추상클래스로 선언
    • 인스턴스 생성 불가
    • 추상 클래스를 상속받으면 서브클래스도 추상클래스(abstract으로 선언 필요)
    • 단, 서브클래스에서 슈퍼클래스의 추상메소드를 구현하면, 구현한 서브클래스는 추상클래스가 아님
    • 목적 : 상속을 통해 슈퍼클래스의 추상메소드를 서브클래스에서 구현하여 다형성 실현
//예시
abstract class Caculator { //추상클래스
		public abstract int cal(int a, int b); //추상메서드(원형만 작성)
}

//추상메서드를 구현한 서브클래스들
class AddCalc extends Caculator {
		@Override //오버라이딩 표시를 해줌으로써 시간복잡성 줄일 수 있음
		public int cal(int a, int b) { return a+b; }
}
class SubCalc extends Caculator {
		@Override
		public int cal(int a, int b) { return a-b; }
}
class AvgCalc extends Caculator {
		@Override
		public int cal(int a, int b) { return (a+b)/2; }
}

public class GoodCalc {
		public static void main(String[] args){
				//Calculator c = new Calculator(); //추상클래스는 인스턴스 생성 불가

				AddCalc a = new AddCalc();
				System.out.println(a.cal(2,3));
				SubCalc s = new SubCalc();
				System.out.println(s.cal(2,3));
				AvgCalc g = new AvgCalc();
				System.out.println(g.cal(2,3));
		}
}

🔷 인터페이스

  • 인터페이스와 추상클래스는 객체가 될 수 없음
    (이유 : 구현이 덜 된 추상메서드가 있어서)
    ⇒ 인터페이스와 추상클래스를 상속(extends)/구현(implements)하는 클래스를 객체로 생성
  • [접근제어자] interface 인터페이스명 { … } : 인터페이스 선언
  • 구성요소 : 상수, 추상메소드 + default메소드, private메소드, static메소드 (필드는 선언 불가)
    interface I {
    		[public static final] int 변수명 = 상수값; //상수 필드
    		[public abstract] 리턴타입 추상메소드명(); //추상 메소드
    		[public] default 리턴타입 디폴트메소드명() { 메소드코드 }; //디폴트 메소드
    		private 리턴타입 프라이빗메소드명() { 메소드코드 }; //프라이빗 메소드
    		[public|private] static 리턴타입 스태틱메소드명() { 메소드코드 }; //스태틱 메소드
    }
    • 상수 필드 : public static final 생략 가능 (public만 허용)
    • 추상 메소드 : public abstract 생략 가능 (메소드 원형만 선언)
    • 디폴드 메소드 : 인터페이스에서 코드가 작성된(구현된) 메소드 (인터페이스를 상속하는 인터페이스/클래스에 자동 상속, public만 허용)
      → 많은 클래스에서 인터페이스를 구현할 때 새로운 추상메소드 생성 시 많은 클래스에서 구현을 해야하는 부담을 줄이기 위해 구현된 메소드인 디폴트 메소드가 보완
    • static 메소드 : 객체 생성없이 인터페이스 타입으로 호출하는 메소드 (public, private 모두 지정가능, 생략 시 public)
    • private 메소드 : 인터페이스 내에 있는 디폴트/정적 메서드에서 사용하기 위해 작성되는 메서드
  • 인터페이스의 레퍼런스 변수 선언 가능 But, 인터페이스 객체 생성 불가
    • 인터페이스명 인터페이스변수; : 인터페이스의 레퍼런스 변수 선언
    • 인터페이스변수 = new 인터페이스명(); : 인터페이스 객체 생성 불가
  • 인터페이스 간 상속 가능 + 다중 상속 허용 : extends 키워드 사용
    interface I1 { void a1(); }
    interface I2 { void a2(); }
    interface I3 extends I1,I2 { void a3(); } //인터페이스I1,I2에 추상메서드 추가한 인터페이스I3
  • 인터페이스를 구현하는 클래스 작성 : implements 키워드 사용
    • 여러 인터페이스 동시 구현 가능(class 클래스명 implements 인터페이스명1,인터페이스명2,…{})

    • 추상메소드는 반드시 구현

    • 디폴트 메소드는 자동 상속 (따로 구현 안해도 됨! 충돌 시 오버라이딩 사용!)

    • 인터페이스 내의 메소드 외에 추가로 메소드 작성 가능

      class A implements I3 {  //클래스A는 인터페이스 I1,I2,I3을 모두 구현
      		//인터페이스의 모든 추상 메소드 구현
      		public void a1() { System.out.println("첫번째 인터페이스"); }
      		public void a2() { System.out.println("두번째 인터페이스"); }
      		public void a3() { System.out.println("세번째 인터페이스"); }
      	
      		//새로운 메소드 추가 가능
      		public void a4() { System.out.println("클래스의 멤버메소드"); }
      }
      
      I3 레퍼런스 = new A(); //업캐스팅 가능
  • 인터페이스 구현 + 슈퍼클래스 상속 가능
    interface I { ... }
    class A { ... }
    class B extends A implements I { ... }
profile
개발, 분석 배운 내용 정리하기!

0개의 댓글