상속이란? : 기존(상위) 클래스의 자산(멤버)을 자식(하위) 클래스에서 재사용하기 위한 것입니다.
상속의 장점
상속의 적용 방법 : extends 키워드 사용
public class Person {
String name;
void eat(){}
void jump(){}
}
public class SpiderMan extends Person { // SpiderMan은 Person을 상속받음
boolean isSpider;
void fireWeb(){}
}
Object 클래스 : 모든 클래스의 조상 클래스
Object.hashCode() : 객체의 주소값을 변환하여 생성한 객체의 고유한 정수 값을 반환Object.equals() : 객체의 주소값을 비교해서 true/false를 boolean 타입으로 반환Object.toString() : 객체의 정보를 String 타입으로 변환해서 반환Object 클래스의 주요 메서드들은 대부분의 클래스에서 Overriding(재정의)해서 사용합니다.
상속 (is a 관계) : 자바는 단일 상속 (Single Inheritance)만 지원합니다. 다중 상속을 하게 되면, 여러 클래스의 기능을 물려 받으면서 동일한 이름의 메서드가 있다면 어떤 메서드를 사용할 것인지와 같은 복잡한 문제가 발생할 수 있습니다.
합성 (has a 관계) : 이러한 단일 상속 관계로 발생하는 단점을 인터페이스(Interface)와 합성(Composition)으로 극복하고 있습니다. (인터페이스는 추후 포스팅에서 다시 자세하게 다루겠습니다.) 포함 관계는 상속받는 대신, 상위 객체를 멤버 변수로 활용하며 클래스의 인스턴스를 활용하는 방식입니다.
현재는 Java의 객체지향 프로그래밍에 대해서 학습하기 위해 상속(extends)를 활용하지만, 아래의 몇가지 이유로 인해 현업에서는 상속을 지양하는 편이며 대신 합성을 사용하는 편입니다.
- 결합도가 높아집니다.
- 불필요한 기능이 상속됩니다.
- 부모 클래스의 결함이 그대로 넘어옵니다.
- 부모 클래스와 자식 클래스의 동시 수정이 문제될 수도 있습니다.
다양한 상속의 활용
public class Person {
String name;
void eat(){}
void jump(){}
}
public class Spider {
void fireWeb() {System.out.println("슉슉!");}
}
public class SpiderMan extends Person { // SpiderMan is a Person <상속>
Spider spider = new Spider(); // SpiderMan has a Spider <합성>
boolean isSpider;
void fireWeb(){
if(this.isSpider) { this.spider.fireWeb(); }
else { System.out.println("사람은 거미줄을 쏠 수 없습니다!); }
}
}
public class Person {
String name;
void eat(){}
void jump(){ System.out.println("두 다리로 힘껏 점프"); }
}
public class Spider {
void fireWeb() {System.out.println("슉슉!");}
}
public class SpiderMan extends Person {
Spider spider = new Spider();
boolean isSpider;
void fireWeb(){
if(this.isSpider) { this.spider.fireWeb(); }
else { System.out.println("사람은 거미줄을 쏠 수 없습니다!); }
}
void jump() { // SpiderMan에서 jump()를 재정의
if(this.isSpider) { spider.jump(); }
else { System.out.println("두 다리로 힘껏 점프"); }
}
}
오버라이딩의 조건
(접근 제한자와 예외에 관한 포스팅은 추후 추가하도록 하겠습니다.)
super 키워드 : 자기 자신에게 접근한 this 키워드와 같이 조상에 접근하기 위해서는 super 키워드를 사용할 수 있습니다. 위의 예시 중 일부를 다시 가져와서 super 키워드를 활용해 보겠습니다.void jump() {
if(this.isSpider) { spider.jump(); }
// else { System.out.println("두 다리로 힘껏 점프"); }
else { super.jump(); } // SpiderMan의 조상인 Person 클래스의 jump를 그대로 활용
}
정리 : 위의 SpiderMan 클래스를 예시로 들어 설명하면
SpiderMan 객체에서 jump 메서드를 호출할 때,
SpiderMan이 갖는 jump를 활용하기 위해서는 this.jump();를 활용,
상속받은 Person이 갖는 jump를 활용하기 위해서는 super.jump();를 활용,
포함된 Spider이 갖는 jump를 활용하기 위해서는 spider.jump();를 활용할 수 있습니다.
변수의 scope : 사용된 위치에서 점점 확장해가며 처음 만난 선언부에 연결됩니다.
class Parent {
String x = "parent";
}
class Child extends Parent {
String x = "child";
void method() {
String x = "method";
System.out.println(x); // 1
System.out.println(this.x); // 2
System.out.println(super.x); // 3
}
}
// 메인 함수
Child c = new Child();
c.method();
// 실행 결과
//
// method // 1번이 print되면서 제일 가까운 x인 method 내의 x를 출력합니다.
// child // 2번이 print되면서 this 키워드를 통해 Child 객체 내의 x를 출력합니다.
// parent // 3번이 print되면서 super 키워드를 통해 조상인 Parent 객체 내의 x를 출력합니다.
this()가 해당 클래스의 다른 생성자를 호출하듯, super()은 조상 클래스의 생성자를 호출합니다. 조상 클래스에서 선언된 맴버들은 super()을 통해 조상클래스의 생성자에서 초기화가 이뤄지도록 합니다.super() 키워드 또한 this()키워드와 마찬가지로 생성자의 첫 줄에서만 호출이 가능합니다. (this()와 super()은 같이 사용할 수 없습니다.)this() 또는 super()를 호출하지 않는 경우, 컴파일러가 super() 키워드를 삽입합니다. 이는 결론적으로 맨 상위의 Object 클래스까지 객체가 다 만들어지는 구조가 됩니다.사전적 의미 : 주석
컴파일러, JVM, 프레임워크 등이 확인하는 용도로 사용됩니다.
소스코드에 메타 데이터를 삽입하는 형태로 소스코드에 붙여놓은 라벨이라고 생각할 수 있습니다.
기본 annotation의 예
(Annotation에 관한 자세한 내용은 추후 포스팅에서 다시 다루도록 하겠습니다.)