티스토리 뷰

목표

자바의 상속에 대해 학습하기

 

학습할 것

  • 자바 상속의 특징

  • super 키워드

  • 메소드 오버라이딩

  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

  • 추상 클래스

  • final 키워드

  • Object 클래스

 

1. 자바 상속의 특징

자바에서는 부모 클래스의 멤버를 자식 클래스에게 물려줄 수 있는데 이를 상속이라고 하며 프로그램에서는 부모 클래스를 상위 클래스라고 부르기도 하고 자식 클래스를 하위 클래스 또는 파생 클래스라고 부른다.

 

상속은 이미 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여주고 또한 클래스의 수정을 최소화시킬 수도 있다. 부모 클래스의 수정으로 모든 자식 클래스들의 수정 효과를 가져오기 때문에 유지 보수 시간을 최소화시켜준다.

 

프로그램에서는 자식이 부모를 선택하게 되며 자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지를 결정하고 선택된 부모 클래스는 다음과 같이 extends 뒤에 기술한다. (다른 언어와는 달리 자바는 다중 상속을 허용하지 않아 여러 개의 부모 클래스를 상속할 수 없어 extends 뒤에는 단 하나의 부모 클래스만 와야 한다.)

class 자식클래스 extends 부모클래스 {
    // 필드
    // 생성자
    // 메소드
}

 

2. super 키워드

 

super 키워드는 부모 클래스로부터 상속받은 필드나 메서드를 자식 클래스에서 참조하는데 사용하는 참조 변수이다.

 

모든 객체는 클래스의 생성자를 호출해야만 생성되는데 자바에서도 자식 객체를 생성하면 부모 객체가 먼저 생성되고 자식 객체가 그 다음에 생성된다. 부모 생성자는 자식 생성자의 맨 첫 줄에서 super 키워드를 사용하여 호출하게 된다.

 

부모 생성자가 자식 생성자 내 명시적으로 선언되어 있지 않으면 컴파일러에 의해 super()가 자동으로 추가하게 되는데 이럴 경우 다음과 같이 부모 클래스에는 부모의 기본 생성자가 존재해야 한다.

public class Parent {
	public Parent() {
    }
}
public class Child extends Parent {
	public Child() {
    	super();
    }
}

만약 자식 생성자를 선언한 후 명시적으로 부모 생성자를 호출하고 싶은 경우에는 직접 자식 생성자를 선언하고 다음과 같은 방법으로 부모 생성자 또한 호출해야 한다.

public class Parent {
    public int index;
    public String name;
    
	public Parent(int index, String name) {
    	this.index = index;
        this.name = name;
    }
}
public Child extends Parent {
	public String value;
    
	public Child(int index, String number, String value) {
    	super(index, number);
        this.value = value;
    }
}

super 키워드를 사용할 때 매개값이 있는 경우 매개값의 타입과 일치하는 부모 생성자를 호출하게 된다.

만약 매개값의 타입과 일치하는 부모 생성자가 없을 경우 컴파일 오류가 발생한다.

 

앞서 말한 것처럼 super 키워드가 자식 생성자 내에서 생략되는 경우 컴파일러에 의해 super()가 자동적으로 추가되기 때문에 부모의 기본 생성자가 존재하지 않고 매개 변수가 있는 생성자만 있는 경우 반드시 자식 생성자에서는 부모 생성자 호출을 위해 super 키워드를 자식 생성자 첫 줄에 명시적으로 호출해야 한다. 

 

public class Parent {
	int value = 10;
}
class Child extends Parent {
    int value = 20;
    
    public void method1() {
    	System.out.println(value);
        System.out.println(super.value);
    }
}

super 키워드는 자식 생성자 내에 선언되어 부모 생성자가 호출될 뿐만 아니라 위와 같이 부모 클래스 내 선언된 필드나 메소드를 호출하는 경우에도 사용될 수 있다. 부모 클래스의 멤버와 자식 클래스의 멤버 이름이 같은 경우 super 키워드를 사용하면 된다.

 

3. 메소드 오버라이딩

상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우 자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다.

 

부모 클래스의 모든 메소드가 자식 클래스에 맞게 설계되어 있다면 좋겠지만 자식 클래스가 사용하기에 적합하지 않을 수 있다.

이러한 경우 상속 받은 메소드를 자식 클래스에서 다시 수정해서 사용하는 것이 메소드 오버라이딩 기능이다.

 

메소드가 오버라이딩 되면 부모 객체의 메소드는 숨겨지고 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

 

메소드를 오버라이딩 할 때는 다음과 같은 규칙을 주의해서 작성해야 한다.

  • 부모의 메소드와 동일한 리턴 타입, 메소드 이름, 매개 변수 리스트를 가져야 한다.
  • 접근 제한을 더 강하게 오버라이딩 할 수 없다.
  • 새로운 예외를 throws 할 수 없다.
public class Parent {
	void method1() { System.out.println("부모 클래스의 메소드입니다."); }
}
public Child extends Parent {
	@Override
	void method1() { System.out.println("자식 클래스의 메소드입니다."); }
}
public class MethodOverridingTest {
	public static void main(String[] args) {
    	Parent p = new Parent();
        p.method1(); // Parent 메소드 호출
        Child c = new Child();
        c.method1(); // Child 메소드 호출
        Parent pc = new Child();
        pc.method1(); // Child 메소드 호출
    }
}

 

위 코드를 보면 자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면 부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용하게 된다. 만약 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황인 경우 앞서 설명했던 super 키워드를 이용해 부모 메소드를 호출하면 된다.

 

4. 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

다이나믹 메소드 디스패치란 메소드 오버라이딩이 되어있을 때 실행 시점에 어떤 메소드를 실행할 지 결정하는 것이다.

 

이를 통해 부모 클래스는 모든 자식 클래스에 공통되는 메소드를 지정하게 되며, 자식 클래스는 이러한 메소드의 일부 또는 전체에 대해 재정의를 하거나 자식 클래스만의 특정 메소드를 구현할 수 있게 된다.

 

Parent p = new Parent();
Child c = new Child();
p = new Child(); // upcasting

자바에서는 다형성을 위해 부모 클래스로의 타입 변환을 허용한다. 즉, 부모 타입에 모든 자식 객체가 대입될 수 있다. 부모 클래스로 타입 변환이 된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능해진다. 이 때 예외가 존재하는데 메소드가 자식 클래스에서 오버라이딩이 되었다면 자식 클래스의 메소드가 대신 호출된다.

 

4. 추상 클래스

객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다. 추상 클래스와 실체 클래스는 상속의 관계를 가지고 있어 추상 클래스가 부모이고 실체 클래스가 자식으로 구현되어 실체 클래스는 추상 클래스의 모든 특성을 물려받고 추가적인 특성을 가질 수 있다.

 

추상 클래스는 실체 클래스의 공통되는 필드와 메소드를 추출해서 만들었기 때문에 객체를 직접 생성해서 사용할 수 없어 new 연산자를 사용해서 인스턴스를 생성시키지 못한다.

 

실체 클래스들의 공통적인 특성을 뽑아내어 추상 클래스로 만드는 이유는 실체 클래스들의 공통한 필드와 메소드의 이름을 통일할 목적과 실체 클래스를 작성할 때 시간을 절약할 수 있는 장점 때문이다. 추상 클래스는 다음과 같이 선언한다.

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

클래스에 abstract를 붙이게 되면 new 연산자를 이용하여 객체를 만들지 못하고 상속을 통해 자식 클래스만 만들 수 있다.

 

추상 클래스는 모든 실체 클래스들이 가지고 있는 메소드의 실행 내용이 동일하다면 추상 클래스에 메소드를 작성하는 것이 좋다.

하지만 실체 클래스마다 메소드 내 실행 내용이 다르다면 아래와 같이 추상 메소드를 선언할 수 있다.

public abstract class Parent {
	public abstract void method1();
}

 

5. final 키워드

final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있으며 final 키워드는 해당 선언이 최종 상태이고 절대 수정할 수 없음을 뜻한다. final 키워드가 클래스, 필드, 메소드 선언에 사용될 경우 해석이 조금씩 달라지게 된다.

 

상속할 수 없는  final 클래스

클래스를 선언할 때 final 키워드를 class 앞에 붙이게 되면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다.

즉 final 클래스는 부모 클래스가 될 수 없어 자식 클래스를 만들 수 없게 되는 것이다.

 

오버라이딩 할 수 없는 final 메소드

메소드를 선언할 때 final 키워드를 붙이게 되면 해당 메소드는 최종적인 메소드이므로 오버라이딩을 할 수 없는 메소드가 된다.

즉 부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의 할 수 없다.

 

6. Object 클래스

클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않는 경우 java.lang.Object 클래스를 상속하게 된다.따라서 자바의 모든 클래스는 Object 클래스의 자식이나 자손 클래스이고 Object는 자바의 최상위 부모 클래스이다.

 

Object 클래스의 API는 docs.oracle.com/javase/7/docs/api/java/lang/Object.html 이곳에서 확인할 수 있다.

 

1. 객체 비교(equals())

public boolean equals(Object obj) { … }

equals() 메소드의 매개 타입은 Object인데 이것은 모든 객체가 매개값으로 대입될 수 있다는 것을 의미한다.

Object 클래스의 equals() 메소드는 두 객체를 비교해서 논리적으로 동등하면 true 그렇지 않으면 false를 리턴한다.

 

논리적으로 동등하다는 것은 같은 객체이건 다른 객체이건 상관 없이 객체가 저장하고 있는 데이터가 동일함을 의미한다.

 

예를 들어 String  클래스가 Object의 equals() 메소드는 String 객체의 번지를 비교하는 것이 아니고 문자열이 동일한지 조사해서 문자열이 같은 경우 true, 그렇지 않으면 false를 리턴하게 되는데 이는 String 클래스가 Object의 equals() 메소드를 재정의하여 번지 비교가 아닌 문자열 비교로 변경했기 때문이다. 그러므로 Object 클래스의 객체 비교 equals() 메소드를 이용할 때 객체 내 특정 필드값을 비교하고 싶다면 equals()를 오버라이딩해야 한다.

public class Member {
	public String id;
    
    public Member(String id) {
    	this.id = id;
    }
    
    @Override
    public boolean equals(Object obj) {
    	if(obj instanceof Member) {
        	Member member = (Member) obj;
            if(id.equals(member.id)) {
            	return true;
            }
       	}
        return false;
   }
}

equals() 메소드를 오버라이딩 할 때는 Object 타입의 매개 변수는 모든 객체가 매개값으로 제공될 수 있기 때문에 instanceof 연산자로 기준 객체와 동일한 타입인지 제일 먼저 확인해야 한다. 만약 비교 객체가 다른 타입이라민 false를 리턴한다.

 

2. 객체 해시코드(hashCode())

객체 해시코드란 객체를 식별할 하나의 정수값을 의미하는데 Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있다. 논리적으로 해시코드의 동등을 비교하기 위해서는 hashCode() 메소드 또한 오버라이딩이 필요하다.

 

컬렉션 프레임워크에 해당하는 HashSet, HashMap, Hashtable의 경우 동등을 비교할 때 hashCode() 메소드를 실행하여 리턴된 해시코드 값이 같으면 equals() 메소드로 다시 비교하게 된다. 그러므로 hashCode() 메소드가 true가 나와도 equals() 메소드의 리턴값이 다르면 다른 객체가 된다.

public class Member {
	public String id;
    
    public Member(String id) {
    	this.id = id;
    }
    
    @Override
    public boolean equals(Object obj) {
    	if(obj instanceof Member) {
        	Member member = (Member) obj;
            if(id.equals(member.id)) {
            	return true;
            }
        }
        return false;
   }
   
   @Override
   public int hashCode() {
   	return id.hashCode();
   }
}

 

3. 객체 문자 정보(toString())

Object 클래스의 toString() 메소드는 객체의 문자 정보를 리턴한다. 객체의 문자 정보란 객체를 문자열로 표현한 값을 의미한다.기본적으로 Object 클래스의 toString() 메소드는 "클래스명@16진수해시코드"로 구성된 문자 정보를 리턴한다.

 

Object 클래스의 toString() 메소드의 리턴값은 자바 애플리케이션에서 별 가치가 없는 정보이므로 toString() 메소드를 오버라이딩하여 간결하고 유익한 정보를 리턴하도록 되어 있다.

public class Member {
	private String company;
    private String name;
    
    public Member(String company, String name) {
    	this.company = company;
        this.name = name;
    }
    
    @Override
    public String toString() {
    	return company + ", " + name;
    }
}
public class MemberTest {
	public static void main(String[] args) {
    	Member member = new Member("Google", "teamo");
        
        String strObj = member.toString();
        System.out.println(strObj);
        
        System.out.println(member);
    }
}

참고로 System.out.println() 메소드를 사용할 때 매개값이 기본 타입(byte, short, int, long, float, double, boolean)일 경우 해당 값을 그대로 출력하고 만약 매개값으로 객체를 주면 객체의 toString() 메소드를 호출해서 리턴값을 받아 출력한다.

 

 

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/09   »
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
글 보관함