java

자바 - 상속

Feb 19, 2020

클래스 확장하기

목차

1. 클래스 확장하기
2. 메서드 오버라이딩
3. 슈퍼클래스 할당
4. 타입 변환
5. final 메서드와 final 클래스
6. 추상 메서드와 추상 클래스
7. 보호 접근
8. 익명 서브클래스
9. 상속과 기본 메서드
10. super를 이용한 메서드 표현식


1. 클래스 확장하기

서브클래스 extends 슈퍼클래스

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    ...
}

public class Manager extends Employee {
    private double bonus;

    public Manager(String name, double salary) {
        super(name, salary);
        bonus = 0;
    }
}



2. 메서드 오버라이딩

슈퍼클래스의 메서드를 서브클래스에서 재정의한다.

public class Employee {
    public void getSalary() {
        return salary;
    }
}

public class Manager extends Employee {
    public void getSalary() {
        return super.getSalary() + bonus;
    }
}



3. 슈퍼클래스 할당

Employee[] staff = new Employee[4];
staff[0] = new Employee("seungui", 2400);
staff[1] = new Employee("jwa", 2800);
staff[2] = new Employee("bongdong", 3300);
// 슈퍼클래스 변수에 할당해도 된다.
staff[3] = new Manager("buminseok", 600); 

이렇게하면 모든 직원에 동작하는 코드를 작성할 수 있다.



4. 타입 변환

서브 클래스 객체를 슈퍼클래스 타입 변수에 할당하면 슈퍼클래스에 있는 메서드만 호출할 수 있다.

Employee emp = new Manager{..};
// 오류 : setBonus는 Employee 클래스에 없는 메서드
emp.setBonus(1000); 

instanceof 연산자와 타입 변환 연산자를 사용해서 타입변환을 할 수 있다.

if(emp instanceof Manager) {
    Manager tmep = (Manager)emp;
    temp.setBonus(1000);
}



5. final 메서드와 final 클래스

메서드를 final로 선언하면 해당 메서드를 오버라이드 할 수 없다.

클래스의 서브 클래스를 만들지 못하게 하고 싶으면 클래스 정의에 final 키워드를 사용한다.



6. 추상 메서드와 추상 클래스

구현이 없는 메서드를 정의해서 서브클래스가 해당 메서드를 구현하도록 강제할 수 있다. 이렇게 구현이 없는 메서드를 추상 메서드라 한다. 추상 메서드가 포함된 클래스를 추상 클래스라 한다.

추상 메서드와 추상 클래스에는 abstract 키워드를 붙여야 한다.

public abstract class Person {
    private String name;

    public Person(String name) { this.name = name; }
    public final String getName() { return name; }

    public abstract int getId();
}

추상 클래스는 인터페이스와 달리 인스턴스 변수와 생성자를 가질 수 있다.

추상 클래스는 클래스를 확장하기 위한 용도이므로 인스턴스를 생성할 수 없다.

하지만 변수에 구체적인 서브클래스의 객체 참조를 담는다면 추상 클래스 타입으로 변수를 선언하는 건 가능하다.

public interface Named {
    default String getName() { return ""; }
}

public class Student extends Person implements Named {
    private int id;

    public Student(String name, int id) { super(name); this.id = id; }
    public int getId() { return id; }
}

psvm() {
    // Person은 추상 클래스이나 서브 클래스의 객체를 담으므로 잘 작동한다.
    Person p = new Student("Fred", 10000);
}

7. 보호 접근

메서드를 서브 클래스 전용으로 제한하고 싶거나, 서브클래스 메서드에서 슈퍼클래스의 인스턴스 변수에 접근하고 싶을 때가 있다. 그럴 때는 클래스의 해당 기능을 protected로 선언하면 된다.

자바에서 protected 접근 권한은 패키지 수준이다. 그러므로 다른 패키지에서 시도하는 접근만 보호한다.



8. 익명 서브 클래스

인터페이스를 구현하는 익명 클래스를 만들 수 있는 것처럼 슈퍼클래스를 확장하는 익명 클래스도 만들 수 있다. 익명 서브클래스는 디버깅에 유용하다.

public class AnonymousSubclassDemo {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<String>(100) {
            public void add(int index, String element) {
                super.add(index, element);
                System.out.printf("Adding %s at %d\n", element, index);
            }
        };

        names.add(0, "Peter");
        names.add(1, "Paul");
        names.add(0, "Mary");
        System.out.println(names);

        invite(new ArrayList<String>() {{ add("Harry"); add("Sally"); }});
    }

    public static void invite(ArrayList<String> friends) {
        System.out.println("Guest list: " + friends);
    }
}

여기서는 ArrayList 익명 서브 클래스를 생성해서 add 메서드를 오버라이드 했다. 인스턴스 초기 용량은 100으로 생성된다. 배열리스트를 다시 쓸 일이 없어서 익명으로 만들고 추가했다. 바깥쪽 중괄호는 익명 서브클래스를 만드는 것이고, 안쪽 중괄호는 초기화 블록이다.

일반적으로는 사용하지 않는다. 이 문법은 사용하지 않는 게 좋다.



9. 상속과 기본 메서드

클래스를 확장하고 인터페이스를 구현하는 클래스가 있는데 메서드 하나가 같은 이름을 가진다면 항상 인터페이스 구현보다 슈퍼클래스 확장이 우선이다. 서브 클래스에서 충돌을 해결할 필요가 없다.

클래스 우선 규칙은 자바 7과 호환성을 보장한다. 따라서 인터페이스에 기본 메서드를 추가해도 기본 메서드가 생기기 전부터 동작하던 코드에는 영향을 주지 않는다.

public interface Named {
    default String getName() {
        return "";
    }
}

public class Person {
    protected String name;

    public String getName() {
        return name;
    }
}

public class Student extends Person implements Named {
}



10. super를 이용한 메서드 표현식

람다 표현식에서 메서드 표현식은 객체 :: 인스턴스메서드 형태를 취할 수 있다고 했다. 그런데 객체 참조 대신 super를 사용할 수도 있다.

super :: 인스턴스메서드

public class Worker {
    public void work() {
        for(int i = 0; i < 100; i++) {
            System.out.println("Working");
        }
    }
}

public class ConcurrentWorker extends Worker {
    public void work() {
        Thread t = new Thread(super::work);
        t.start();
    }
}