1. 상속
클래스간 공통된 속성과 기능이 많이 있을 경우 만약 이 공통된 부분을 클래스마다 다시 쓴다면 프로그래밍의 중요한 법칙 중 하나인 ‘DRY(Don’t Repeat Yourself; 중복 배체)’를 어기게 되는 것입니다. 자바의 ‘클래스 상속(Class Inheritance)’ 기능이 이 문제를 해결해줍니다.
public class 자식클래스 extends 부모클래스 {
...
}
접근 제어자가 private이 아니면 자식클래스에서도 변수와 메소드를 그대로 사용할 수 있습니다.
자식 클래스가 부모 클래스가 가지고 있는 메소드를 덮어 쓰고 싶을 때는(기존 부모 클래스의 메소드와는 독립적인 메소드로 만들고 싶을 때)
‘메소드 오버라이딩(Method Overriding)’을 해줘야 합니다. 메소드 정의 위에 써져있는 @Override가 메소드 오버라이딩을 표시해줍니다. @Override와 같이 골뱅이(@)가 붙어있는 문법을 ‘어노테이션(Annotation)’이라고 합니다. 주석(Comment)과 어느정도 비슷하지만, 어노테이션은 자바에서 추가적인 기능을 제공합니다. 예를 들어서 @Override를 써줬는데 부모 클래스에 같은 이름의 메소드가 없는 경우, 오류가 나오게 됩니다.
public class MinimumBalanceAccount extends BankAccount {
private int minimum;
@Override
public boolean withdraw(int amount) {
if (getBalance() - amount < minimum) {
System.out.println("적어도 " + minimum + "원은 남겨야 합니다.");
return false;
}
setBalance(getBalance() - amount);
return true;
}
}
이번에는 부모 클래스가 가지고 있는 메소드에서 몇 가지를 추가해서 쓰고 싶은 경우에는 super를 사용
하면 됩니다.
public class TransferLimitAccount extends BankAccount {
private int transferLimit;
@Override
boolean withdraw(int amount) {
if (amount > transferLimit) {
return false;
}
return super.withdraw(amount);
}
}
2. 캐스팅과 제네릭
1) 캐스팅
ArrayList<BankAccount> accounts = new ArrayList<>();
accounts.add(ba);
accounts.add(mba);
accounts.add(sa);
for (BankAccount account : accounts) {
account.deposit(1000);
}
이렇게 하면 각 계좌가 BankAccount 타입으로 ‘캐스팅(Casting)’되고, 한꺼번에 묶어서 다룰 수 있습니다.
sa에게는 이자를 붙여주고 싶은데, BankAccount 클래스에는 addInterest 메소드가 없습니다. 만약 여기서 SavingsAccount만 골라서 addInterest 메소드를 쓰고 싶으면 instanceof 키워드를 사용하면 됩니다.
for (BankAccount account : accounts) {
account.deposit(1000);
if (account instanceof SavingsAccount) {
((SavingsAccount) account).addInterest();
}
}
2) 제네릭
아래 꺽쇠 기호(<>) 사이에 있는 T를 ‘타입 파라미터’라고 부릅니다. 그리고 이와 같이 타입 파라미터를 받는 클래스를 ‘제네릭 클래스(Generic Class)’라고 합니다.
public class Box<T> {
private T something;
public void set(T object) {
this.something = object;
}
public T get() {
return something;
}
}
아래처럼 타입 파라미터로 String을 넘겨주면,
Box<String> box = new Box<>();
클래스에 있던 모든 T가 String으로 대체된다고 생각하면 됩니다.
public class Box<String> {
private String object;
public void set(String object) {
this.object = object;
}
public String get() {
return object;
}
}
지금까지는 타입 파라미터로 아무 클래스나 넘길 수 있었는데요. extends 키워드를 이용하면 타입을 제한할 수도 있습니다.
public class PhoneBox<T extends Phone> extends Box<T> {
public void handsFreeCall(String numberString) {
object.call(numberString);
}
}
3. 인터페이스와 추상 클래스
1) 인터페이스
클래스가 생성될 때, 특정 빈 메소드를 강제로 가지도록 하고 싶을 때 인터페이스를 이용합니다
// 인터페이스
public interface Shape {
// 빈 메소드
double getArea();
double getPerimeter();
}
// 특정 인터페이스를 따라야 하는 클래스
public class Circle implements Shape {
...
...
public double getArea() {
return PI * radius * radius;
}
public double getPerimeter() {
return 2 * PI * radius;
}
}
2) 추상 클래스
// 추상클래스
public abstract class Shape {
// 변수
public double x, y;
// 메소드
public void move(doulbe x, double y) {
...
}
// 빈 메소드 (추상 메소드)
public abstract double getArea();
public abstract double getPerimeter();
}