부모 클래스에 만들어진 필드, 메소드를 자식클래스가 물려받음. 동일 특성을 재정의할 필요 없어 자식 클래스 간결해짐.
public class Person{
}
public class Student extends Person { //Person을 상속받는 클래스 Student
}
public class StudentWorker extends Student{ //Student를 상속받는 StudentWorker
}
★ extends
키워드 사용 ★
java.lang.Object
클래스public
: 모든 클래스 접근 허용디폴트
: 패키지 내 모든 클래스에 접근 허용protect
: 같은 패키지 내 모든 클래스 접근 허용, 다른 패키지에 있어도 서브 클래스는 슈퍼 클래스의 protected 멤버 접근 가능private
: 다른 모든 클래스에 접근 불허, 클래스 내 멤버들에게만 허용Q. 서브 클래스 객체가 생성될 때, 서브와 슈퍼 클래스의 생성자가 모두 실행되는가?
A. Yes. 둘 다 실행됨. 서브 클래스 객체 생성 시 이 객체에 서브 클래스 멤버와 슈퍼 클래스 멤버가 모두 들어있다.
Q. 서브 클래스의 생성자와 슈퍼 클래스의 생성자 중 누가 먼저 실행되는가?
A. 슈퍼 클래스 -> 서브 클래스 순서로 실행된다.
호출 순서 : 서브 클래스의 생성자가 먼저 호출 -> 서브 클래스의 생성자가 실행 전 슈퍼 클래스 생성자 호출 -> 슈퍼 클래스 생성자 실행 -> 서브 클래스 생성자 실행
super()
이용: 서브 클래스 객체를 슈퍼 클래스 타입으로 타입 변환
class Person { … }
class Student extends Person { … }
Student s = new Student();
Person p = s; // 업캐스팅, 자동타입변환
설명))
그냥 슈퍼클래스를 쓴다는 게 아니라
Person p;
Student s = new Student("이재문");
p=s; //업캐스팅
이게 레퍼런스 p가 Student 객체의 멤버 중에서 그 슈퍼클래스인 Person의 멤버만 접근한다는 의미!!
"Student 객체 멤버 중 슈퍼클래스 멤버만 접근"이란 의미가 중요
다양한 자식 객체들을 부모의 타입으로 묶어서 관리할 수 있다!
Square s = new Square();
Triangle t = new Triangle();
Circle c = new Circle();
//업 캐스팅
Shape[] shapes = {s, t, c};
각각 다른 타입이었던 객체들을 상속 관계를 맺어 부모 클래스(Shape)로 업캐스팅이 가능하다면 하나의 타입으로 묶어 배열을 구성할 수 있다!
그래서 묶어서 뭐할 건데?
만약 각 도형의 넓이를 구하고 싶을 경우, 하나의 도형의 넓이를 구할 때는 큰 문제가 되지 않지만 정사각형, 삼각형, 원 모든 도형의 넓이를 구하고 싶을 경우 넓이를 구하는 공식이 다르기 때문에
s.area();
t.area();
c.area();
이렇게 각 메서드를 호출해줘야 하는 번거로움이 발생한다.
이 경우 코드량도 늘어나고 가독성도 떨어지게 된다.
그러나 부모 클래스에 있는 area() 메서드를 각 자식 클래스에 오버라이딩하여, 업캐스팅된 각 객체가 본인 도형 타입에 맞는 공식을 쓸 수 있게 된다면?
(만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행)
for(int i = 0; i < shapes.length; i++){
shapes[i].area();
}
하나의 자료형으로 관리하므로 코드량도 줄어들고 가독성도 좋아지며 유지보수성도 좋아진다!
이것이 자바에서 업캐스팅과 오버라이딩을 사용하는 이유이자 다형성을 실현하는 방법인 것이다.
업캐스팅을 할 경우 자식 클래스 내 고유한 메서드 실행은 불가능하다
-> 따라서 업캐스팅한 객체를 다시 자식 클래스 타입으로 되돌리는 다운 캐스팅(downcasting)이 필요하다
: 슈퍼 클래스 객체를 서브 클래스 타입으로 변환. 개발자의 명시적 타입 변환 필요.
class Person {...}
class Student extends Person {...}
...
Person p = new Student("이재문"); //업캐스팅
...
Student s = (Student)p; //다운캐스팅, (Student)의 타입 변환 표시 필요
업캐스팅된 레퍼런스로 객체의 타입을 판단하기가 어려움 -> 슈퍼 클래스는 여러 서브 클래스에 상속되기 때문
instanceof 연산자
레퍼런스가 가리키는 객체의 타입 식별을 위해 사용
객체레퍼런스 instanceof 클래스타입
연산 결과 : true/false의 불린 값
슈퍼 클래스의 메소드를 서브 클래스에서 재정의 (슈퍼 클래스의 메소드 이름, 매개변수 타입과 개수, 리턴 타입 등 모든 것을 동일하게 작성)
<동적 바인딩 발생!>
서브 클래스에 오버라이딩된 메소드가 무조건 실행되는 동적바인딩 (슈퍼 클래스 메소드가 무시, 덮어쓰기로 번역되기도 함)
오버라이딩 : 슈퍼 클래스에 선언된 메소드를 각 서브 클래스들이 자신만의 내용으로 새로 구현
super
: 슈퍼 클래스의 멤버를 접근할 때 사용되는 레퍼런스
슈퍼 클래스의 메소드를 호출
컴파일러는 super의 접근을 정적 바인딩
으로 처리
정적바인딩 : 컴파일 시간에 성격이 결정
동적바인딩 : 실행 시간(run time)에 성격이 결정
super() 메서드 : 부모 클래스의 생성자 호출
Student s = new Student("해리", 18, "hogwarts");
class Person {
protected String name;
protected int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected String schoolName;
public Student(String name, int age, String schoolName){
super(name, age); //부모 클래스의 생성자 호출
this.schoolName = schoolName;
}
}
: 선언되어 있으나 구현되지 않은 메소드, abstract
로 선언됨
public abstract String getName();
pubilc abstract void setName(String s);
추상 메소드는 서브 클래스에서 오버라이딩하여 구현해야함
//1. 추상 메소드 하나 이상 가진 클래스
abstract class Shape { // 추상 클래스 선언
public Shape() { }
public void paint() { draw(); }
abstract public void draw(); // 추상 메소드
}
//2. 추상 메소드가 없는 클래스
abstract class MyComponent { // 추상 클래스 선언
String name;
public void load(String name) {
this.name = name;
}
}
★ 추상 클래스는 객체를 생성할 수 없다!★
추상 클래스 상속의 2가지 경우
추상 클래스의 단순 상속 - 서브 클래스도 추상 클래스
-> 추상 클래스 상속받고 추상 메소드는 구현하지 않은 경우, 서브 클래스도 abstract로 선언해야함
추상 클래스 구현 상속 - 서브 클래스는 추상 클래스 X
-> 서브 클래스에서 슈퍼 클래스의 추상 메소드 구현 (오버라이딩), 서브 클래스는 추상 클래스가 아님
클래스가 구현해야 할 메소드들이 선언되는 추상형
-특정 역할에 대한 대략적인 틀만 정의
interface
키워드로 선언함
public interface SerialDriver {...}
인터페이스의 추상 메소드를 모두 구현한 클래스 작성
implements
키워드 사용class SamsungPhone implements PhoneInterface { // 인터페이스 구현
// PhoneInterface의 모든 메소드 구현
public void sendCall() { System.out.println("띠리리리링"); }
public void receiveCall() { System.out.println("전화가 왔습니다."); }
// 메소드 추가 작성
public void flash() { System.out.println("전화기에 불이 켜졌습니다."); }
}
=> 인터페이스는 스펙을 주어 클래스들이 그 기능을 서로 다르게 구현할 수 있도록 하는 클래스의 규격 선언이며, 클래스의 다형성을 실현하는 도구이다.
인터페이스를 구현하는 객체는 인터페이스 타입으로 업캐스팅 될 수 있음!
interface AAA(){
public void aaa();
}
interface BBB(){
public void bbb();
}
interface CCC(){
public void ccc();
}
//Alphabet 클래스는 총 3가지 역할(AAA, BBB, CCC)을 수행
class Alphabet implements AAA, BBB, CCC {
...
}
//Alphabet 클래스의 객체 생성
Alphabet alpha = new Alphabet();
//인터페이스 타입으로 업캐스팅 (즉, alpha 객체를 a라는 인터페이스 타입으로 해석할 수 있다는 의미)
AAA a = alpha;
BBB b = alpha;
CCC c = alpha;
인터페이스 타입으로 해석된 객체는 해당 인터페이스의 메소드만 수행 가능
a.aaa(); //O
b.bbb(); //O
c.ccc(); //O
a.bbb() //X
c.aaa(); //X
업캐스팅을 사용하는 장점 => "그룹화" 가능!
interface Flyable {
public void fly();
}
class Bird implements Flyable {
public void fly() {
...
}
}
class Airplane implements Flyable {
public void fly() {
...
}
}
//다양한 객체 생성
Bird bird = new Bird();
Airplane airplane = new Airplane();
//인터페이스 타입으로 그룹화!
Flyable[] flyanbleThings = { bird, airplane };
+) ArrayList를 통해서도 업캐스팅하여 인터페이스 타입으로 저장 가능
//ArrayList를 통해서도 객체들을 업캐스팅하여 저장 가능
ArrayList<Flyable> flyList = new ArrayList<Flyable>();
list.add(bird);
list.add(airplane);