💡 상속을 통해 유지 보수가 쉽고, 중복을 방지하며, 프로그램의 수정 및 추가가 유연한 프로그램을 만들 수 있다.

부모 클래스는 추상적이고 자식 클래스는 구체적이어야 한다.class 자식 클래스명 extends 부모 클래스명 { ... }
class A {
...
}
class B extends A {
...
}
💡 클래스 상속의 활용과 이점을 예제를 통해 알아보자.
setSpeed(), String을 반환하는 drive() 메서드가 포함되어야 한다.시속 100Km로 승용차를 운전합니다.
시속 80Km로 화물차를 운전합니다.
시속 60Km로 버스를 운전합니다.
class Car {
private int speed;
private String type;
public Car () {
setSpeed(100);
type = "승용차";
}
public void setSpeed(int speed) {
this.speed = speed;
}
public String drive() {
return "시속 " + speed + "Km로 " + type + "를 운전합니다.";
}
}
class Truck {
private int speed;
private String type;
public Truck () {
setSpeed(80);
type = "화물차";
}
public void setSpeed(int speed) {
this.speed = speed;
}
public String drive() {
return "시속 " + speed + "Km로 " + type + "를 운전합니다.";
}
}
class Bus {
private int speed;
private String type;
public Bus () {
setSpeed(60);
type = "버스";
}
public void setSpeed(int speed) {
this.speed = speed;
}
public String drive() {
return "시속 " + speed + "Km로 " + type + "를 운전합니다.";
}
}
public class VehicleTest2 {
public static void main(String[] args) {
Car myCar = new Car();
Truck myTruck = new Truck();
Bus myBus = new Bus();
System.out.println(myCar.drive());
System.out.println(myTruck.drive());
System.out.println(myBus.drive());
}
}class Vehicle{
protected int speed;
protected String type;
public Vehicle () {}
public void setSpeed(int speed) {
this.speed = speed;
}
public String drive() {
return "시속 " + speed + "Km로 " + type + "를 운전합니다.";
}
}
class Car extends Vehicle {
public Car () {
speed = 100;
type = "승용차";
}
}
class Truck extends Vehicle {
public Truck () {
speed = 80;
type = "화물차";
}
}
class Bus extends Vehicle {
public Bus () {
speed = 60;
type = "버스";
}
}
public class VehicleTest {
public static void main(String[] args) {
Car myCar = new Car();
Truck myTruck = new Truck();
Bus myBus = new Bus();
System.out.println(myCar.drive());
System.out.println(myTruck.drive());
System.out.println(myBus.drive());
}
}💡 상속은 다양한 장점을 갖는다.
super예약어를 통해 부모 클래스의 필드와 메서드를 쉽게 활용할 수 있다.super(); 를 사용public class Parent{
public Parent() { // 기본 생성자: 파라미터가 없는 생성자(NoAurgument Constructor)
System.out.println("부모 생성자");
}
}
public class Child extends Parent{
public Parent(){
super(); //명시적으로 생성자를 호출함
System.out.println("부모 생성자");
}
}
public class Parent{
public Parent() {
System.out.println("부모 생성자");
}
}
public class Child extends Parent{
public Child(){
// super(); 묵시적으로 기본 생성자를 호출함, 컴파일러가 default로 호출
System.out.println("부모 생성자");
}
}
public class Parent{
private int age;
public Parent(int age) {
this.age = age;
System.out.println("부모 생성자, 나이: " + this.age);
}
}
public class Child extends Parent{
public Child(){
/* 컴파일 에러 발생 */
// super(); defatul로 기본생성자 호출되는대 부모클래스에 기본생성자가 없기 때문
System.out.println("부모 생성자");
}
}
따라서 코드를 리팩토링(refactoring)하면 다음과 같습니다.
public class Child extends Parent{
public Child(){
/* 컴파일 에러 해결 */
super(50);
System.out.println("부모 생성자");
}
}
함수의
파라미터를 달리해서함수를 작성하는 것이고
대표적인 예로System.out.println()이 있습니다.
→ 즉, 매개변수의 갯수나 데이터 타입은 다르고, 함수명은 동일하게 메서드를 정의하는 것
class Calculator {
// 예 1
int sum(int x, int y){
return x + y;
}
// 예2 2
double sum(double x, double, y){
return x + y;
}
// 예 3 → error
int sum(int x, double y){
return x + (int)y;
}
// 예 4 → error
double sum(int x, double y){
return x + y;
}
}
이름이 같아야 한다.갯수나 데이터 타입이 달라야 한다.리턴타입이 다른 경우, 오버로딩이 성립하지 않는다.성립되지 않는다.💡 자식클래스가 부모클래스의 메소드를 자신의 필요에 맞추어 재정의한다.
단, "함수 바디"만 달리 하여야한다.
class Shape{ public void draw() { System.out.println("Shape"); } }
class Circle extends Shape {
@Override
public void draw() { System.out.println("Circle을 그립니다."); }
}
class Rectangle extends Shape {
@Override
public void draw() { System.out.println("Rectangle을 그립니다."); }
}
class Triangle extends Shape {
@Override
public void draw() { System.out.println("Triangle을 그립니다."); }
}
Shape 클래스를 부모로 가지는 Circle, Rectangle, Triangle 클래스들은 Shape의 draw() 메서드를 활용가능하지만, 위의 표기처럼 같은 이름, 같은 매개변수와 결과 값을 가진 새로운 메소드로 재정의 가능하며 자식을 객체로 불러왔을 때, 오버라이딩된 자식의 메소드로 사용된다.

다형성 = 상속 + 오버라이딩
객체 지향 원리이다.
class Shape{
public void draw() {
System.out.println("도형을 그립니다.");
}
}
class Rectangle extends Shape{
@Override
public void draw() {
System.out.println("사각형을 그립니다.");
}
public void onlyRecFunc(){
System.out.println("오직 사각형클래스의 함수입니다.");
}
}
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("삼각형을 그립니다.");
}
}
public class ShapeTest {
public static void main(String[] args) {
// 1번
Shape shape = new Shape();
shape.draw();
// 2번
Rectangle rectangle = new Rectangle();
rectangle.draw();
// 3번
Triangle triangle = new Triangle();
triangle.draw();
}
}

이와 같이 상속 관계인 상위 클래스의 함수, 변수를 덮어쓰는 것이 다형성이다.
Shape s = new Rectangle(); // (Shape) new Rectangle();💡 캐스팅 : 형변환을 의미
사전적 의미
- 업캐스팅 (UpCasting) : 자손타입의 참조변수를 조상타입의 참조변수로 변환하는 것
- 다운캐스팅 (DownCasting) : 조상타입의 참조변수를 자손타입의 참조변수로 변환하는 것
일반적 의미
- 업캐스팅: 다형성을 적용한 것
- 다운캐스팅: 다형성 적용으로 잃어버린 특성을 복구시키기 위해 원래 상태로 되돌리는 것
앞선 Shape-Rectangle 관계를 표현한 코드에서
Shape s = new Rectangle();
/* 컴파일 에러 */
s.onlyRecFunc(); // Rectangle클래스에만 선언된 함수인 onlyRecFunc()호출 불가!
// -> 다형성 적용으로 인해 Rectangle클래스의 특성잃어버림
Shape s = new Rectangle();
/* 컴파일 에러해결 */
Reactangle r = (Rectangle) s;
r.onlyRecFunc(); // Rectangle클래스에만 선언된 함수인 onlyRecFunc() 호출 가능!
// -> 다운캐스팅을 통해 잃어버린 Rectangle클래스의 특성 복구 완료!
package Book;
public class Book {
private String name;
private String publisher;
// 기본생성자
public Book(){
this.name = "";
this.publisher = "";
}
// 파라미터 필요로하는 생성자
public Book(String name, String publisher){
this.name = name;
this.publisher = publisher;
}
public void print(){
System.out.println("print : Book");
};
}class Novel extends Book{
private String name;
private String publisher;
public Novel(String name, String publisher){
super(name, publisher); // Book(String name, String publisher) 호출
}
@Override
public void print(){
System.out.println("print : Novel"); // Body만 변경
}
}업캐스팅(자식클래스 객체를 부모 클래스로 형변환)하여 객체 선언(부모 = 자식;)
Book b = new Novel("메타버스 소설", "출판사(IT)"); // (Book) new Novel("메타버스 소설", "출판사(IT)")
부모클래스(Book)으로 생성된 객체의 멤버 함수를 호출
Book b = new Novel("소설","소설출판사");
b.print(); // 출력=> print : Novel

부모 클래스를 참조 변수로 하여 자식 클래스 객체 생성한 것을 가리킬 수 있다.
다음 그림은 업캐스팅 시 스택(좌), 힙(우) 메모리 영역을 보여준다.
참조변수 a 가 가리키고 있는 위치에서 부모 클래스(A)에 할당된 영역만큼 접근할 수 있다.
그러니 그 외의 부분 B에 종속된 함수(printB())를 사용할 수 없는 것이다.

데이터 타입이 B 클래스이고, 만약, B의 기능(함수)을 사용하려고 하는데
참조변수인 b가 참조하는 곳에는 클래스 A의 멤버(필드, 메서드) 밖에 없기 때문에 추후 b에서 클래스 B에만 할당된 맴버에 접근하는 것을 미연에 방지하고자 컴파일 에러를 발생시킵니다.
현재 힙 영역에 클래스 A의 맴버만 올라가 있는 이유는
new A();했기 때문이다.
다운캐스팅이 가능합니다. A a = new B();
B b = (A) a;
