Java를 사용하면 객체지향 프로그래밍(Object-Oriented Programming, OOP)을 지향하게 되며, 그 과정에서 클래스(class)와 인터페이스(interface)를 자연스럽게 자주 사용하게 된다.
클래스는 객체를 생서하기 위한 설계도 이다.
필드(속성)와 메서드(행동)을 정의하고, 이를 바탕으로 객체(인스턴스)를 생성한다.
이처럼 현실 세계의 개념을 코드로 추상화하여 구조화하는 방식을 객체지향 프로그래밍(OOP)이라고 하며, 클래스는 그 중심에 있는 핵심 요소이다.
// 사람(Person)을 표현하는 클래스
public class Person {
// 필드: 객체의 속성(상태)을 나타냄
String name;
int age;
// 생성자: 객체 생성 시 호출되는 특별한 메서드
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 메서드: 객체의 동작(행위)을 나타냄
public void introduce() {
System.out.println("안녕하세요, 저는 " + name + "이고, 나이는 " + age + "살입니다.");
}
}
Person 클래스는 name과 age라는 두개의 속성을 가진다.
생성자 Person(String, int)는 객체를 만들 때의 초기값을 설정하기 위해 존재한다.
introduce()는 객체가 수행할 수 있는 행동(메서드)이다.
아래는 main() 메서드에서 객체를 생성하고 사용하는 예시이다.
// 메인 메서드: 프로그램 실행 진입점
public static void main(String[] args) {
// Person 객체 생성 (인스턴스화)
Person student = new Person("홍길동", 20);
// 메서드 호출
student.introduce(); // 출력: 안녕하세요, 저는 홍길동이고, 나이는 20살입니다.
}
new 키워드를 사용하여 student 객체를 생성하고, 홍길동이라는 이름과 20이라는 나이를 부여한 모습을 확인할 수 있다.
인터페이스는 클래스가 구현해야 할 공통된 동작(메서드)을 정의하는 틀이다.
구체적인 구현은 하지 않으며,
“이 메서드를 반드시 구현해야 한다”
는 약속(계약)의 역할을 한다.
자바는 다중 상속을 허용하지 않지만, 인터페이스는 여러 개를 구현할 수 있으므로 구조적으로 유연한 설계가 가능하다.
인터페이스는 공통된 규약을 정의해서 여러 클래스들이 동일한 방식으로 동작하도록 강제하고, 코드의 유연성과 확장성을 보장한다.
예를 들어보자.
고양이, 강아지, 사자 등의 동물들은 자신만의 울음소리를 가지고 있다
그렇다면, 공통된 작업인 '울음소리'의 소리가 각기 달라서 묶어서 다룰수가 없는 문제가 발생한다
이런 문제를 인터페이스로 해결이 가능하다.
interface Animal {
void sound(); // 모든 동물은 sound()라는 울음소리를 내야 함
}
// 각 동물이 인터페이스를 구현
class Dog implements Animal {
@Override
public void sound() {
System.out.println("멍멍");
}
}
class Cat implements Animal {
@Override
public void sound() {
System.out.println("냐옹");
}
}
class Lion implements Animal {
@Override
public void sound() {
System.out.println("으르렁");
}
}
각기 다른 동물들을 하나의 타입(Animal)로 다를 수 있다
코드가 확장 가능, 유지보수가 쉽고, 다형성이 가능하다.
즉, 클래스만 있으면 중복되고, 통일되지 않고 바꾸기 힘든 메서드들을
인터페이스라는 공통 동작의 표준을 정의하여 설계의 유연성과 확장성을 확보한다.
@Override ?
이 메서드는 상위 클래스 또는 인터페이스의 메서드를 정확히 오버라이딩 한것이라고 컴파일러에게 알려주는 안전장치이다.
만약 위의 코드에서public void souund() { }와 같이 오타가 발생했을때 Override가 없으면 아무일도 일어나지 않는다
souund라는 새로운 메서드가 생성되었다고 판단할 수 있기 때문이다
하지만 @Override를 붙이면 오타가 있거나 메서드 시그니처가 다르면 에러가 발생한다
즉, 실수를 즉시 방지할 수 있어 버그 사전 예방
Vehicle.java
public interface Vehicle {
void drive();
void stop();
void speedUp();
}
Car.java
public class Car implements Vehicle {
private int speed = 0;
@Override
public void drive() {
System.out.println(" 차가 출발합니다. 현재 속도: " + speed + "km/h");
}
@Override
public void stop() {
speed = 0;
System.out.println("차가 멈췄습니다.");
}
@Override
public void speedUp() {
speed += 10;
System.out.println("속도를 올립니다! 현재 속도: " + speed + "km/h");
}
}
CarMain.java
public class CarMain {
public static void main(String[] args) {
Vehicle myCar = new Car();
myCar.drive();
myCar.speedUp();
myCar.speedUp();
myCar.drive();
myCar.stop();
}
}
Vehicle 인터페이스는 명확하게 차량의 기본 동작들을 정의
-> 차량이라면 이런 기능은 공통적으로 들어가야해 라는 행동 규약
Car 클래스는 그 동작을 충실하게 구현
-> 실제 자동차 객체의 동작을 구현
CarMain 클래스는 인터페이스 타입으로 다형성을 활용
-> 프로그램을 실행하고, 인터페이스를 통해 객체를 사용