객체 지향 프로그래밍에서 클래스 간의 관계를 표현하는 기능 중 하나
어떤 클래스가 다른 클래스의 특성(필드와 메서드)을 상속받을 수 있으며 이것은 코드의 재사용성을 높이고, 코드의 확장성을 향상시킨다.
Extends (확장)
extends 키워드를 사용하여 한 클래스가 다른 클래스를 확장한다.
이렇게 하면 자식 클래스는 부모 클래스의 모든 필드와 메서드를 상속받습는다.
class Parent {
// 부모 클래스의 멤버
}
class Child extends Parent {
// 자식 클래스가 부모 클래스를 확장
}
단일 상속만 지원
Java에서는 단일 상속만을 지원한다. 즉, 하나의 클래스는 하나의 직접적인 부모 클래스만을 가질 수 있다.
다중 상속이 일어날 경우, 클래스 간의 모호성과 충돌 문제가 발생할 수 있기 때문에 Java는 이를 허용하지 않는다.
super
super 키워드는 자식 클래스에서 부모 클래스의 멤버에 접근하는 데 사용. super를 통해 부모 클래스의 메서드, 필드를 호출할 수 있다.
super는 부모 클래스의 필드나 메서드를 가리킬 때 사용
class Parent {
int x;
void printX() {
System.out.println(x);
}
}
class Child extends Parent {
int y;
void printXY() {
super.printX(); // 부모 클래스의 메서드 호출
System.out.println(y);
}
}
super() (수퍼 생성자)
super()는 자식 클래스에서 부모 클래스의 생성자를 호출하는 데 사용.
자식 클래스의 생성자에서 super()를 사용하면 부모 클래스의 생성자를 호출하여 초기화 작업을 수행할 수 있다.
class Parent {
Parent() {
// 부모 클래스의 생성자
}
}
class Child extends Parent {
Child() {
super(); // 부모 클래스의 생성자 호출
}
}
재사용
상속은 코드의 재사용성을 높이는 주요 기법 중 하나.
부모 클래스에서 정의한 필드와 메서드를 자식 클래스가 상속받아 사용함으로써 중복 코드를 피하고 효율적인 코드 관리를 가능케 한다.
추상 클래스는 하나 이상의 추상 메서드를 포함하고 있는 클래스다.
추상 메서드는 선언만 있고 본체가 없는 메서드로, 하위 클래스에서 반드시 구현되어야 한다. 추상 클래스는 객체를 직접적으로 생성할 수 없다.
추상 메서드
추상 클래스는 추상 메서드를 가질 수 있다. 추상 메서드는 선언만 있고 본체가 없는 메서드로, 하위 클래스에서 반드시 구현되어야 한다.
객체 생성 불가능
추상 클래스는 객체를 직접적으로 생성할 수 없다. 추상 클래스의 주된 목적은 하위 클래스에서 공통된 특성을 추출하고 상속을 통해 코드의 재사용성을 높이는 것이다.
일반 메서드와 필드
추상 클래스는 추상 메서드 외에도 일반 메서드와 필드를 가질 수 있다. 이러한 일반 메서드와 필드는 하위 클래스에서 공통적으로 사용되는 기능을 제공한다.
추상 클래스 구현
추상 클래스를 상속받는 하위 클래스는 추상 메서드를 반드시 구현해야 한다. 만약 하위 클래스에서 추상 메서드를 구현하지 않으면 해당 클래스도 추상 클래스로 선언되어야 한다.
활용
추상 클래스는 공통된 특성을 가진 클래스들의 구조를 정의하고, 이를 상속받아 구체적인 동작을 갖는 클래스를 만들 때 활용된다.
// 추상 클래스 선언
abstract class Shape {
int x, y;
// 추상 메서드
abstract void draw();
// 일반 메서드
void move(int newX, int newY) {
x = newX;
y = newY;
System.out.println("Moving to (" + x + ", " + y + ")");
}
}
// 추상 클래스를 상속받는 구체적인 클래스
class Circle extends Shape {
int radius;
// 추상 메서드 구현(override)
void draw() {
System.out.println("Drawing a circle with radius " + radius);
}
}
// 추상 클래스를 상속받는 다른 클래스
class Square extends Shape {
int side;
// 추상 메서드 구현(override)
void draw() {
System.out.println("Drawing a square with side " + side);
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
// 추상 클래스를 상속받은 객체 생성
Circle circle = new Circle();
circle.radius = 5;
circle.move(3, 4);
circle.draw();
Square square = new Square();
square.side = 6;
square.move(1, 2);
square.draw();
}
}
객체 지향 프로그래밍에서 클래스가 가져야 할 메서드들의 명세를 정의하는데 사용되는 추상화된 형태이다. 인터페이스는 클래스가 반드시 구현해야 하는 메서드들의 선언을 담고 있다.
코드의 재사용성과 다형성을 높이기 위한 중요한 도구로 사용된다. 각 클래스가 특정 인터페이스를 구현함으로써 해당 인터페이스에서 정의한 동작을 보장받게 되며, 이는 유연하고 확장 가능한 코드의 기반을 제공한다.
인터페이스 선언
인터페이스는 interface 키워드를 사용하여 선언된다. 인터페이스 내에는 추상 메서드, 상수, 디폴트 메서드, 정적 메서드 등을 선언할 수 있다.
void draw(); // 추상 메서드
int MAX_SIZE = 100; // 상수 (public static final 생략 가능)
default void resize() {
System.out.println("Resizing the object."); // 디폴트 메서드
}
static void printInfo() {
System.out.println("Interface info."); // 정적 메서드
}
}
인터페이스 구현
클래스는 implements 키워드를 사용하여 인터페이스를 구현할 수 있다. 인터페이스의 모든 추상 메서드를 반드시 구현해야 한다.
class Circle implements Drawable {
public void draw() {
System.out.println("Drawing a circle.");
}
}
다중 인터페이스 구현
클래스는 여러 개의 인터페이스를 동시에 구현할 수 있다. 모든 인터페이스의 메서드를 구현해야 한다.
class Square implements Drawable, Resizable {
public void draw() {
System.out.println("Drawing a square.");
}
public void resize() {
System.out.println("Resizing a square.");
}
}
인터페이스 간 상속
인터페이스도 다른 인터페이스를 확장(상속)할 수 있습니다. 이 때 extends 키워드를 사용한다.
interface Resizable extends Drawable {
void resize();
}
활용
인터페이스는 클래스에게 어떤 동작을 해야 한다는 규약을 제공하므로, 클래스가 해당 동작을 구현하면서 코드의 일관성과 재사용성을 높일 수 있다.
public class Main {
public static void main(String[] args) {
Drawable circle = new Circle();
circle.draw();
Resizable square = new Square();
square.resize();
}
}
특징
추상 메서드만 포함: 인터페이스는 추상 메서드의 집합으로 이루어져 있습니다. 모든 메서드는 본체가 없이 선언만 있다.
다중 상속 지원: 여러 개의 인터페이스를 동시에 구현할 수 있다. Java에서 다중 상속을 지원하지 않는 클래스 상속과는 달리 유용한 특징 중 하나다.
상수 정의: 인터페이스는 상수를 정의할 수 있다. 이는 주로 인터페이스에서 사용할 상수 값들을 명시하는데 사용된다.
디폴트 메서드와 정적 메서드: Java 8부터는 인터페이스에서 디폴트 메서드와 정적 메서드를 정의할 수 있게 되었다. 이를 통해 기존의 인터페이스를 확장하면서도 하위 호환성을 유지할 수 있다.