상속(Inheritance)이란?
- 상속에 대해서 다루기 전에 우선, OOP(Object-Oriented Programming, 객체지향프로그래밍)가 무엇인지에 대해 알고 있어야한다.
- OOP는 다음과 같은 특징을 가지고 있다.
- 상속과 인터페이스 (계층성)
- 다형성, 사용편의성 (모듈화)
- 캡슐화, 정보은닉
- 자료 추상화 (모델링)
- 동적 바인딩
- OOP는 다음과 같은 특징을 가지고 있다.
- 아래 그림을 통해, 상속에 대해 이해해보자.
- 상자 하나하나는 모두 객체(Object)이고, 자바 용어로는 Class이다.
- 이러한 계층을 표현하기 위해 만들어진 것이 바로 상속이다.
- 상속 시, 하위 객체(자식)는 상위 객체(부모)의 특징(메소드, 변수 등)을 물려받게 된다.
- 이 때, 상위 객체(부모)의 특징(메소드, 변수 등)을 '새롭게 구현'하는가, '그대로 사용'하는가에 따라서 상속의 형태가 갈리게 된다.
- extends
- 부모에서 선언/정의를 모두하며, 자식은 오버라이딩 할 필요 없이 부모의 메소드/변수를 그대로 사용할 수 있다.
- "부모의 특징을 연장해서 사용한다."라고 기억하면 될 듯!
- implements (interface 구현)
- 부모 객체는 선언만 하며, 정의는 반드시 자식이 오버라이딩해서 사용한다.
- "부모의 특징을 도구로 사용해 새로운 특징을 만들어 사용한다."라고 기억하면 될 듯!
- abstract
- extends와 interface의 혼합이다.
- extends를 사용하지만, 몇 개는 추상 메소드로 구현되어 있다.
- extends
1. extends
- 사실 extends가 상속의 대표적인 형태이다.
- 모든 선언/정의를 부모가 하며, 자식은 오버라이딩 할 필요 없이 부모의 메소드/변수를 그대로 사용할 수 있다.
- 물론, 필요에 따라 오버라이딩 해도 상관 없다.
- 예제 코드
class Vehicle {
protected int speed = 3;
public int getSpeed(){
return speed;
}
public void setSpeed(int speed){
this.speed = speed;
}
}
class Car extends Vehicle{
public void printspd(){
System.out.println(speed);
}
}
public class ExtendsSample {
public static main (String[] args){
Car A = new Car();
System.out.println(A.getSpeed());
A.printspd();
}
}
Car 클래스는 Vehicle 클래스의 변수와 메소드를 상속 받았다.
따라서, Car 클래스는 Vehicle 클래스의 speed, getSpeed(), setSpeed()를 사용할 수 있다.
하지만, Java는 "다중상속"을 지원하지 않는다.
즉, 부모 클래스가 두 개 이상 존재할 수 없다는 것이다.
ex) public class Son extends Father, Mother {...}
하지만, Java는 implements를 사용해 여러 interface를 상속 받을 수 있다.
2. implements
- 부모는 선언만 하며, 반드시 자식이 정의를 오버라이딩해서 사용한다.
- 예제 코드
interface TestInterface{
public static int num = 8;
public void fun1();
public void fun2();
}
class InterfaceExam implements TestInterface{
@Override
public void fun1(){
System.out.println(num);
}
@Override
public void fun2() {
}
}
public class InterfaceSample{
public static void main(String args[]){
InterfaceExam exam = new InterfaceExam();
exam.fun1();
}
}
사실, "이러한 구현은 부모의 메소드를 어차피 재정의해야하니 '상속'의 의미가 없지 않나?"라고 할 수 있지만,
Java와 c#의 인터페이스 상속은, 계약 및 분류의 의미가 강하다고 한다.
정리
- extends는 클래스 한 개만 상속 받을 수 있으며, 자식 클래스는 부모 클래스의 기능을 사용할 수 있다.
- implements는 여러 개의 interfaces를 상속 받을 수 있으며, 자식 클래스는 부모의 기능을 다시 정의해서 사용해야한다.
3. Abstract Class(추상 클래스)
Must be so if has at least one abstract method (a class can be abstract even if it has no abstract methods, but that’s rare)
추상 클래스는 추상적으로 정의된 클래스입니다.
추상적으로 정의되었다는 것은, 추상 메소드(abstract method)를 사용한다는 것입니다.
추상 메소드를 쓰지 않는 추상 클래스도 있겠지만, 거의 없을 것이고 그것은 추상적으로 정의했다는 원래의 의도와는 맞지 않은 것이겠죠!
그렇다면 추상 메소드는 무엇일까요?
추상 메소드는 구현해놓지 않은 메소드를 의미합니다. abstract void draw(); 처럼요. 추상 메소드 선언은 추상 클래스에서만 허용됩니다.
예제 코드
abstract class Predator{
private Integer age;
//getter, setter
public abstract String getFood();
public void sleep(){
System.out.println("Predator sleeps");
}
}
interface Barkable {
void bark();
}
class Tiger extends Predator implements Barkable {
@Override
public String getFood() {
return "apple";
}
@Override
public void bark() {
System.out.println("Heung..");
}
}
public class inheritanceTest {
@Test
void implementsTest(){
Tiger jane = new Tiger();
jane.bark();
jane.sleep();
System.out.println(jane.getFood());
jane.setAge(5);
System.out.println("age: "+ jane.getAge());
}
}
결과
Heung..
Predator sleeps
apple
age: 5
abstract class Predator로 추상 클래스를 선언하였고, public abstract String getFood();로 추상 메소드를 선언하였습니다.
4. 추상 클래스와 인터페이스의 차이
추상 클래스와 인터페이스는 비슷해 보입니다.
둘 다 객체를 만들 수 없고, 상속해서 사용해야 하죠.
그러나 몇 가지 차이가 있습니다.
추상 클래스는 멤버 변수를 가질 수 있고, 일부 함수는 구현할 수 있습니다.
인터페이스는 멤버 변수를 가질 수 없고, 함수는 선언만 가능하죠. 사실 인터페이스는 모든 메소드가 추상 메소드입니다.
두 개의 차이는 알겠는데, 그래도 추상 클래스가 인터페이스를 충분히 대체할 수 있다는 생각이 듭니다.
인터페이스는 여러 개를 상속할 수 있다는 것 말고, 어떤 차이가 있는지 궁금했습니다.
여러 글을 읽어보고 고민해본 결과, 추상 클래스와 인터페이스의 근본적 차이가 있음을 깨닫게 되었습니다.
추상 클래스는 IS - A "~이다"라는 의미가 강하고, 인터페이스는 HAS - A "~을 할 수 있는"이라는 의미가 강합니다[3].
위의 사진에서 인간과 동물은 생명체(Creature)를 상속하고 각 생명체는 구분에 따라 인간과 동물을 상속합니다. 그리고 각각 할 수 있는 기능들을 인터페이스로 구현되어있습니다. Swimable을 보시면, 사람인 Kevin, 동물인 Turtle은 할 수 있으나 다른 동물인 Pigeon은 할 수 없습니다. 이를 추상 클래스로 만들려고 해도, 불가능할 것입니다. 그들의 상속 관계가 꼬이겠죠. 추상 클래스는 하나만 상속 가능합니다. 이런 경우 당연히 Swimable을 인터페이스로 쓸 수밖에 없습니다.
정리하자면, 각각 다른 추상 클래스를 상속하는데 공통된 기능이 필요하다면 해당 기능은 인터페이스로 구현해야 합니다. 이런 경우는 Has-A가 될 것입니다. IS-A의 의미가 강하면 추상 클래스의 extends 관계로 상속받을 수 있을 테니까요.
잘 정리된 내용이 있어 아래에 첨부합니다[4][5].
추상 클래스는 부모와 자식 즉, 상속 관계에서 상속(extends)받으며 같은 부모 Class(여기서는 Abstract Class)를 상속받는 자식 Class 간에 공통 기능을 각각 구현하고, 확장하며 상속과 관련되어 있다. 상속은 SuperClass의 기능을 이용, 확장하기 위해 사용된다.
인터페이스는 부모, 자식 관계인 상속 관계에 얽매이지 않고, 공통 기능이 필요 할 때, Abstract Method를 정의해놓고 구현(implements)하는 Class에서 각 기능을 Overridng 하여 여러 가지 형태로 구현할 수 있기에 다형성과 연관되어 있다. Interface는 해당 Interface를 구현하는 Class들에 대해 동일한 method, 동작을 강제하기 위해 존재한다.
상속은 조상 클래스의 기능을 이용하거나 확장하기 위해서 사용되고, 다중 상속의 모호함 때문에 하나만 상속할 수 있습니다. 인터페이스는 해당 인터페이스를 구현한 객체들에 대해서 동일한 동작을 보장받기 위함입니다.
그리고 추상 클래스와 인터페이스에 대해 깊이 있는 논쟁을 기록한 글이 있었습니다.
행위의 틀과 immutable 한 속성만으로 제한해서 유연성과 사용성을 극대화한 것이 자바의 "interface" 입니다.
라는 것이 작성자님의 의견입니다. 인터페이스는 is-a를 위한 것이 아니라, has-a를 위한 것이고, 어떤 행위를 할지 틀을 제공하여 method를 구현할 수 있도록 보장한 것이라는 말씀을 하고 계십니다. 이해에 많은 도움이 되어서 게시글을 첨부합니다.
https://hamait.tistory.com/638?category=79137
출처 : https://wooono.tistory.com/261
https://passionate.tistory.com/9
'프로그래밍 > Java' 카테고리의 다른 글
[Java] Base64 디코딩해서 apk 파일 실행 (1) | 2024.08.30 |
---|---|
[Java] Boolean과 boolean의 차이점 (0) | 2023.06.12 |
[Java] StringTokenizer를 이용해 문자열분리 (0) | 2023.05.05 |
[Java] Scanner / next() 와 nextLine() 차이 (1) | 2023.05.05 |
[Java] String to JsonArray 변환 (0) | 2023.04.13 |
댓글