Notice
Recent Posts
Recent Comments
05-17 21:28
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Byeol Lo

Java - Inheritance 상속 본문

Programming Language/Java

Java - Inheritance 상속

알 수 없는 사용자 2022. 10. 13. 21:32

 현실에서 상속이라는 것은 부모의 자산을 자식이 대신 물려받는 것이다. 객체 지향 프로그램에서도 상속이라는 개념이 사용된다. 클래스 간의 부모 자식의 관계를 명시할 수 있다. 프로그램에서는 부모 클래스를 상위 클래스라고 부르기도 하고, 자식 클래스를 하위 클래스라고 부르기도 한다. 다음은 extends 를 사용해 B를 A에 상속시키는 예제이다.

// A.java
pubilc class A {
    int field1=100;
    void main() {
    
    }
}

// B.java
public class B extends A {
	void main() {}
}

// App.java

public class App {
    public static void main(String[] args) {
    	B b = new B();
        
        System.out.println(b.field1);
    }
}

정상적으로 상속이 된 것을 볼 수 있다.

 

클래스 상속

 현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다. 이때 객체 참조 형태로 상속 관계에 있는 클래스 중에 자식 클래스를 가져오려고 할때, 부모 객체가 먼저 heap 영역에 생성이 되고 자식 객체가 생성된다. 모든 객체는 클래스의 생성자를 호출해야만 생성된다. 그러면 부모객체를 생성할때, 생성자를 어디서 호출한 것인지 의문이 들 수 있다. 이것은 자식의 생성자에 숨어 있다. 부모 생성자는 자식 생성자의 맨 첫 줄에서 호출 된다. 이는 컴파일러에 의해 자동적으로 생성된다. 다음은 그것을 활용한 예제이다.

//A.java
public class A {
    String name;
    
    public A(String name) {
    	this.name = name;
    }
}

//B.java
public class B extends A {
    public B(String name) {
    	super(name);
    }

    public String getName() {
    	return this.name;
    }
    
    public void setName(String name) {
    	this.name = name;
    }
}

//App.java
public class App {
    public static void main(String[] args) {
    	B b = new B("Byeollo");
        
        System.out.println(b.getName());
        
        b.setName("Lobyeol");
        
        System.out.println(b.getName());
    }
}

이때 부모의 생성자를 사용하려면 super라는 것을 써야한다.

메소드의 재정의 @Override

 부모 클래스의 모든 메소드가 자식 메소드에 맞게 설정되면 좋겠지만, 우리는 일부분 수정하고 싶은게 생길 수도 있다. 이때는 자식 클래스에서 수정해서 사용해야한다. 해당 글을 읽기 전에 어노테이션 Annotation을 먼저 보고 오길 바란다. 메소드를 다시 재정의하려면 다음과 같은 규칙을 지켜야 한다.

  • 부모의 메소드의 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 리스트)를 가져야 한다.
  • 접근 제한을 더 강하게 오버라이딩할 수 없다.
  • 새로운 예외(Exception)를 throws할 수 없다.

다음은 그 예제이다.

//Calculator.java
public class Calculator {
	double pi = 3.141592;
    
    public double circle(double r) {
    	return r*r*this.pi;
    }
}

//Computer.java
public class Computer {
	@Override
    public double circle(double r) {
    	return r*r*Math.PI;
    }
}

//Main.java
public class Main {
	public static void main(String[] args) {
    	Computer com = new Computer();
        Calculator cal = new Calculator();
        
        System.out.println(com.circle(10));
        System.out.println(cal.circle(10));
    }
}

 

부모 메소드 호출 super

 아까 자식 객체를 생성할때, 부모 객체가 먼저 생성된다고 했다. 그럼 해당 부모 객체를 어떻게 불러올 수 있을까? super라는 변수에 저장되게 된다. 이걸 통해서 부모의 메소드에 접근이 가능하다. 이런 기능은 오버라이드 된 함수 대신 기존 함수를 자식 객체에서 사용하고 싶을때 사용한다.

final 클래스와 final 메소드

 final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있다. final은 최종이라는 의미이며, 전 게시물에서 봤듯이 한번 저장되면 수정이 불가능하다. 클래스와 메소드에 final을 선언 시에는 상속과 관련이 있게 된다. 클래스를 선언할 때는 최종 클래스가 되는 것이므로 상속할 수 없는 클래스가 되고, 메소드에 선언할 때에는 오버라이딩을 할 수 없는 메소드가 된다.

protected 접근 제한자

 protected는 클래스 외의 곳에서 사용가능하다. 필드, 생성자, 메소드에서 사용 가능하다는 말이다. 같은 패키지에서는 default와 같이 접근 제한이 없지만 다른 패키지에서는 자식 클래스만 접근을 허용한다. 다음 예제를 보자.

package package1;

public class A{
	protected String field;
    
    protected A() {
    
    }
    
    protected void method() {}
}
package package1;

public class B{
	public void method() {
    	A a = new A();
        
        a.field = "value";
        
        a.method();
    }
}
package package2;

import pakcage1.A;

public class C{
	public void method(){
    	A a = new A(); // x
    }
}
package package2;

import package1.A;

public class D extends A{
	public D {
    	super();
        
        this.field = "value";
        
        this.method();
    }
}

즉 두번째를 제외하고는 된다. 요약하자면 같은 패키지 내에서, 자식에서는 된다.

 

클래스 다형성

 다형성은 같은 타입이지만 실행 결과가 다양한객체를 이용할 수 있는 성질을 말한다. 코드 측면에서 보면 다형성은 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 해준다. 따라서, 자바는 부모 타입의 객체에 모든 자식 객체가 대입될 수 있다. 어떤 자동차의 부품 중에 타이어라는 객체를 넣을때, 그 자식들을 다음과 같이 넣을 수 있다.

public class Car {
    Tire t1 = new SpecialTire();
    Tire t2 = new FancyTire();
}

 이때 자동타입 변환이라는게 일어나는데, 이를 Promotion이라고 한다. Promotion은 자식 객체는 부모 객체를 상속 받았기 때문에 부모객체는 자식 객체로 바꿀 수 있는 성질이다.

 다형성에는 또 다른 특이한 성질이 있다. 불러온 부모 객체를 Ancester의 A 자식 객체를 Descendent의 D라고 하자. D에서는 A의 method2를 override한 메소드가 있다고 하자. 이때, Promotion을 했을때, method2는 누구의 것을 참조하게 될까? 바로 D이다.

class Example{
    public static void main(String[] args) {
    	D d = new D();
        
        A a = d;
        
        a.method2(); // D의 override 된 함수 메소드 호출
        
    }
}

 

배열로 객체 관리

 객체 안에는 다양한 부품들이 들어갈 수 있다. 그 중에 유독 하나의 부품들이 많이 들어갈 수 있는데, 이 객체명 필드명 = new 객체명(); 을 일일히 다 적어주어야 한다. 이때, 배열을 선언하여 관리할 수 있다.

class Room {
	Table[] tables = {
    	new Table();,
        new Table();,
        new Table();,
        new Table();
    }
}

 

매개변수의 다형성

 Promotion은 메소드에 많이 발생하는데, 주로 변수명에 해당 객체의 자식 객체가 들어갈 때이다. 이때 들어간 객체자체가 가지고 있는 오버라이딩 된 메소드들에 따라 함수의 구동 방식이 달라진다.

 

강제 타입 변환(Casting)

 자식 타입이 부모 타입으로 자동 변환하면, 부모 타입에 선언된 필드와 메소드만 사용 가능하다는 제약사항이 따른다. 이때 자식 타입의 메소드나 필드를 다시 사용하고 싶으면, Casting을 해야한다. 이때, 강제 타입 변환은 부모 타입으로 변환되어 있는 상태에서만 가능하다. 그러면 부모 객체가 참조하는 객체가 부모 객체인지 자식 객체인지 확인하는 방법이 필요한데, 그것이 instanceof로 확인 가능하다. 만약 메소드 내에서 매개변수의 Casting을 하려고 할때, 확인을 해주지 않으면 ClassCastException이 발생할 수 있다.

 

 추상 클래스

 추상(abstract)는 공통되는 특성을 추출한 것을 말한다. 예를 들어 아기, 노인, 청년, 직장인의 공통점은 사람이라는 공통점이 있다. 이런 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다. 추상 클래스와 실체 클래스는 상속의 관계에 있으며, 추상 클래스의 모든 특성을 물려받은 클래스는 추가적인 특성을 지닐 수 있다. 또한, 추상 클래스는 extends 뒤에만 쓰이는 클래스이다.

 이러한 추상 클래스의 용도는 두가지가 있는데, 첫째로 실제 클래스들의 공통된 필드와 메소드의 이름을 통일할 목적. 두번째로 실체 클래스를 작성할 때 시간을 절약하기 위함이다. 추상 클래스를 작성할 때는 기존 객체와는 다르게 선언한다.

public abstract class 클래스명{
    //필드
    //생성자
    //메소드
}

 abstract만 추가해주면 추상 객체가 된다. 나머지 블록 내의 필드, 생성자, 메소드는 전부 동일하다.

 

추상 메소드

 추상메소드도 추상의 의미만 알면 쉽게 이해가 가능하다. 예를들어, 모든 사람들이 말을 할 수 있는건 사실이다. 하지만, 간혹 매우 낮은 확률로 말을 할 수 없는 경우도 생긴다. 이때는 추상 클래스 Person에 추상 메소드로 speaking이라는 것을 미리 선언을 할 수가 없게 된다. 따라서, 해당 경험을 반영하여 자바에는 추상 메소드까지 지원한다. 마찬가지로 abstract만 접근제한자 뒤에 붙여주면 된다.

 

 

Comments