제 5 장 상속과 다형성
상속 (inheritance) 상속 슈퍼 클래스에서 하위 클래스로 갈 수록 구체적 상속은 코드 재사용을 위한 중요한 기법입니다. 상속 (inheritance) 상속 상위 클래스의 특성 (필드, 메소드)을 하위 클래스에 물려 주는 것 슈퍼 클래스 (superclass) 특성을 물려주는 상위 클래스 서브 클래스 (subclass) 특성을 물려 받는 하위 클래스 슈퍼 클래스에 자신만의 특성(필드, 메소드) 추가 슈퍼 클래스의 특성(메소드)을 수정 : 구체적으로 오버라이딩이라고 부름 슈퍼 클래스에서 하위 클래스로 갈 수록 구체적 예) 폰 -> 모바일폰 -> 뮤직폰 -> 스마트폰 상속을 통해 서브 클래스의 간결한 클래스 정의 동일한 특성을 재정의할 필요가 없어 클래스 정의가 간결 해짐
상속 관계 예 전화 걸기 class Phone 전화 받기 상속 class MobilePhone 무선 기지국 연결 배터리 충전하기 구체화 상속 음악 다운받기 음악 재생하기 class MusicPhone 상속 class SmartPhone 무선인터넷 지도검색
상속의 필요성 클래스 사이의 멤버 중복 선언 방지 필드와 메소드 재사용으로 클래스 간결화 클래스 간 계층적 분류 및 관리 상속이 없는 경우 중복된 멤버를 가진 5개의 클래스 class Person class Student class StudentWorker class Researcher class Professor 말하기 먹기 걷기 잠자기 말하기 먹기 걷기 잠자기 공부하기 말하기 먹기 걷기 잠자기 공부하기 일하기 말하기 먹기 걷기 잠자기 연구하기 말하기 먹기 걷기 잠자기 연구하기 가르치기 상속의 필요성 말하기 먹기 걷기 잠자기 class Person 상속을 이용한 경우 중복이 제거되고 간결해진 클래스 구조 클래스 사이의 멤버 중복 선언 방지 필드와 메소드 재사용으로 클래스 간결화 클래스 간 계층적 분류 및 관리 상속 공부하기 연구하기 class Researcher class Student 상속 상속 일하기 가르치기 class Professor class StudentWorker
클래스 상속과 객체 상속 선언 자바 상속의 특징 public class Person { ... } 다중 상속 지원하지 않는다 다수 개의 클래스를 상속받지 못한다. 상속의 횟수에 제한을 두지 않는다 계층구조의 최상위에 있는 클래스는 java.lang.Object 클래스이다. 모든 클래스는 자동으로 java.lang.Object를 상속받는다. public class Person { ... } public class Student extends Person { // Person을 상속받는 클래스 Student 선언 public class StudentWorker extends Student { // Student를 상속받는 StudentWorker 선언
예제 5-1 : 클래스 상속 만들어 보기 (x,y)의 한 점을 표현하는 Point 클래스와 이를 상속받아 컬러 점을 표현하는 ColorPoint 클래스를 만들어보자. class Point { int x, y; // 한 점을 구성하는 x, y 좌표 void set(int x, int y) { this.x = x; this.y = y; } void showPoint() { // 점의 좌표 출력 System.out.println("(" + x + "," + y + ")"); public class ColorPoint extends Point { // Point를 상속받은 ColorPoint 선언 String color; // 점의 색 void setColor(String color) { this.color = color; } void showColorPoint() { // 컬러 점의 좌표 출력 System.out.print(color); showPoint(); // Point 클래스의 showPoint() 호출 public static void main(String [] args) { ColorPoint cp = new ColorPoint(); cp.set(3,4); // Point 클래스의 set() 메소드 호출 cp.setColor("red"); // 색 지정 cp.showColorPoint(); // 컬러 점의 좌표 출력 red(3,4)
자바의 클래스 계층 구조 자바에서는 모든 클래스는 반드시 java.lang.Object 클래스를 자동으로 상속받는다.
상속 : Diagram 모든 클래스는 Object 클래스로부터 직, 간접적으로 확장한다. 객체 계좌 저축계좌
Object의 메소드
equals 메소드 equals 메소드는 두 객체가 같은지를 비교한다. 기본형끼리 동등성을 비교할 때는 간단하게 == 연산자를 사용하면 된다. 그러나 == 연산자로 객체를 비교하면 두 객체가 같은 대상을 가리킬 때만 true를 리턴한다. 완전히 같은 내용을 가지는 객체라도 참조값이 다르면 다르다고 평가한다. 객체끼리 내용으로 비교할 수 있는 별도의 비교 방법이 필요한데 이것이 바로 equals 메소드이다.
Object의 toString()를 재정의 public class Car { private String model; public Car(String model) { this.model = model; } public String toString() { return “모델: ” + model; Object의 toString()를 재정의
toString 메소드의 재정의 To provide a nicer representation of an object, override toString: public String toString() { return "BankAccount[balance=" + balance + "]"; } This works better: BankAccount momsSavings = new BankAccount(5000); String s = momsSavings.toString(); // Sets s to "BankAccount[balance=5000]" Big Java by Cay Horstmann Copyright © 2008 by John Wiley & Sons. All rights reserved.
서브 클래스의 객체와 멤버 사용 서브 클래스의 객체와 멤버 접근 서브 클래스의 객체는 슈퍼 클래스의 멤버도 포함 슈퍼 클래스의 private 멤버는 상속되지 않음 서브 클래스에서 직접 접근 불가 슈퍼 클래스의 prviate 멤버는 슈퍼 클래스의 메소드를 통해서 만 접근 가능 서브 클래스 객체에 슈퍼 클래스 멤버가 포함되므 로 슈퍼 클래스 멤버의 접근은 서브 클래스 멤버 접근과 동일
상속 class Car { int speed; } class SportsCar extends Car int turbo; 상속한다는 의미
서브 클래스가 수퍼 클래스를 포함(집합개념)
상속의 예 General Specific
상속 계층 예: Swing 계층 Superclass JComponent has methods getWidth, getHeight AbstractButton class has methods to set/get button text and icon
슈퍼 클래스 A와 서브 클래스 B 그리고 인스턴스 관계 public class A { public int p; private int n; public void setN(int n) { this.n = n; } public int getN() { return n; public static void main(String [] args) { A a = new A(); B b = new B(); } a b p n setN() getN() public class B extends A { private int m; public void setM(int m) { this.m = m; } public int getM() { return m; public String toString() { String s = getN() + “ “ + getM(); return s; p n setN() getN() m setM() getM() toString() main() 실행 중 생성된 인스턴스 슈퍼 클래스 A와 서브 클래스 B 그리고 인스턴스 관계
서브 클래스의 객체 멤버 접근 a b p n setN() getN() p n setN() getN() m setM() public class MemberAccessExample { public static void main(String[] args) { A a = new A(); B b = new B(); a.p = 5; a.n = 5; // n 은 private 멤버, 컴파일 오류 발생 b.p = 5; b.n = 5; // n 은 private 멤버, 컴파일 오류 발생 b.setN(10); int i = b.getN(); // i는 10 b.m = 20; // m은 private 멤버, 컴파일 오류 발생 b.setM(20); System.out.println(b.toString()); // 화면에 10 20이 출력됨 } a b p n setN() getN() p n setN() getN() m setM() getM() toString() 10 20
상속과 접근 지정자 자바의 접근 지정자 4 가지 private 멤버 protected 멤버 public, protected, default, private 상속 관계에서 주의할 접근 지정자는 private와 protected private 멤버 슈퍼 클래스의 private 멤버는 서브 클래스 포함하 여 모든 클래스에서 접근 불허 protected 멤버 같은 패키지 내의 모든 클래스는 접근 동일 패키지 여부와 상관없이 서브 클래스에서 슈 퍼 클래스의 멤버 접근 가능
슈퍼 클래스 멤버의 접근 지정자 default private protected public 같은 패키지의 클래스 O X 같은 패키지의 서브 클래스 다른 패키지의 클래스 다른 패키지의 서브 클래스
같은 패키지 내 상속 관계에서 접근 패키지 A i pro pri pub b public class A { int i; protected int pro; private int pri; public int pub; } public class B extends A { void set() { i = 1; pro = 2; pri = 3; // private 멤버 접근 불가, 컴파일 오류 발생 pub = 4; } public static void main(String[] args) { B b = new B(); b.set(); i pro pri pub b
다른 패키지의 상속 관계에서 접근 패키지 PA 패키지 PB i pro pri pub b public class A { int i; protected int pro; private int pri; public int pub; } 패키지 PA public class B extends A { void set() { i = 1; // i는 default 멤버, 컴파일 오류 발생 pro = 2; pri = 3; // private 멤버 접근 불가, 컴파일 오류 발생 pub = 4; } public static void main(String[] args) { B b = new B(); b.set(); 패키지 PB i pro pri pub b
예제 5-2: 상속 관계에 있는 클래스 간 멤버 접근 클래스 Person을 아래와 같은 멤버 필드를 갖도록 선언하고 클래스 Student는 클래스 Person을 상속받아 각 멤버 필드에 값을 저장하시오. 이 예제에서 Person 클래스의 private 필드인 weight는 Student 클래스에서는 접근이 불가능하여 슈퍼 클래스인 Person의 getter와 setter를 통해서만 조작이 가능하다. int age; public String name; protected int height; private int weight; class Person { int age; public String name; protected int height; private int weight; public void setWeight(int weight) { this.weight = weight; } public int getWeight() { return weight; public class Student extends Person { void set() { age = 30; name = "홍길동"; height = 175; setWeight(99); } public static void main(String[] args) { Student s = new Student(); s.set();
서브 클래스와 슈퍼 클래스의 생성자 호출 및 실행 관계 질문 1> 서브 클래스의 인스턴스가 생성될 때 서브 클래스의 생성자와 슈퍼 클래스의 생성자가 모두 실행되는가? 아니면 서브 클래스의 생성자만 실행되는가? 둘 다 실행된다. 질문 2> 서브 클래스의 인스턴스가 생성될 때 서브 클래스의 생성자와 슈퍼 클래스의 생성자의 실행 순서는 어떻게 되는가? 슈퍼 클래스의 생성자가 먼저 실행된 후 서브 클래스의 생성자가 실행된다 new에 의해 서브 클래스의 객체가 생성될 때 슈퍼클래스 생성자와 서브 클래스 생성자 모두 실행됨 호출 순서 서브클래스의 생성자가 먼저 호출되고 실행되기 전 슈퍼 클래스의 생성자 가 호출됨 실행 순서 슈퍼 클래스의 생성자가 먼저 실행되고 서브 클래스의 생성자 실행됨
슈퍼클래스와 서브 클래스의 생성자간의 호출 및 실행 관계 class A { public A() { System.out.println(“생성자A"); } 생성자 호출 생성자 실행 리턴 class B extends A { public B() { System.out.println("생성자B"); } 예상 실행 결과는 ? 생성자 호출 생성자는 서브 클래스의 생성자가 먼저 호출되지만 계속하여 슈퍼 클래스의 생성자를 호출하고, 최상위 슈퍼 클래스의 생성자가 실행되면서 아래로 최하위 서브 클래스의 생성자가 실행되는 과정을 거친다. 생성자 실행 생성자A 생성자B 생성자C class C extends B { public C() { System.out.println("생성자C"); } 생성자 호출 생성자실행 public class ConstructorEx { public static void main(String[] args) { C c; c = new C(); } 위 코드는 모두 ConstructorEx.java 파일에 저장된다.
서브 클래스와 슈퍼 클래스의 생성자 짝 맞추기 슈퍼 클래스와 서브 클래스 슈퍼 클래스와 서브 클래스의 생성자 사이의 짝 맞추기 각각 여러 개의 생성자 가능 슈퍼 클래스와 서브 클래스의 생성자 사이의 짝 맞추기 서브클래스의 객체 생성 시, 실행 가능한 슈퍼 클래스와 서브 클 래스의 생성자 조합 컴파일러는 서브 클래스의 생성자를 기준으로 아래 표와 같은 슈퍼 클래스의 생성자를 찾음 경우 1, 3 개발자가 서브 클래스의 생성자에 슈퍼 클래스의 짝을 지정하는 방법 경우 2, 4 super() 키워드 이용 경우 1 2 3 4 서브 클래스 기본 생성자 매개 변수를 가진 생성자 슈퍼 클래스
1: 슈퍼클래스(기본생성자),서브클래스(기본생성자) 아래 코드는 모두 ConstructorEx2.java 파일에 저장된다. 아래 코드는 모두 ConstructorEx2.java 파일에 저장된다. class A { public A() { System.out.println(" 생성자A"); } public A(int x) { ..... class A { public A(int x) { System.out.println(" 생성자A"); } 서브 클래스의 생성자가 기본생성자인 경우 컴파이일러는 자동으로 슈퍼클래스의 기본생성자와 짝 맺음 컴파일러가 public B()에 대한 짝을 찾을 수 없음 class B extends A { public B() { System.out.println("생성자B"); } class B extends A { public B() { System.out.println("생성자B"); } public class ConstructorEx2 { public static void main(String[] args) { B b; b = new B(); } public class ConstructorEx2 { public static void main(String[] args) { B b; b = new B(); } 생성자A 생성자B 컴파일러에 의해 “Implicit super constructor A() is undefined. Must explicitly invoke another constructor” 오류 메시지가 발생
3:서브 클래스에 매개변수 있는 생성자는 슈퍼클래스의기본생성자와 짝을 이룸 class A { public A() { System.out.println(" 생성자A"); } public A(int x) { System.out.println("매개변수생성자A"); class B extends A { public B() { System.out.println("생성자B"); } public B(int x) { System.out.println("매개변수생성자B"); 옆의 코드는 모두 ConstructorEx3.java 파일에 저장된다. public class ConstructorEx3 { public static void main(String[] args) { B b; b = new B(5); } 생성자A 매개변수생성자B
super() super() 서브 클래스에서 명시적으로 슈퍼 클래스의 생성 자를 선택 호출할 때 사용 사용 방식 super(parameter); 인자를 이용하여 슈퍼 클래스의 적당한 생성자 호출 반드시 서브 클래스 생성자 코드의 제일 첫 라인에 와야 한다.
super()를 이용한 사례 옆의 코드는 모두 ConstructorEx4.java 파일에 저장된다. 매개변수생성자A5 class A { public A() { System.out.println(" 생성자A"); } public A(int x) { System.out.println(“매개변수생성자A" + x); 옆의 코드는 모두 ConstructorEx4.java 파일에 저장된다. class B extends A { public B() { System.out.println("생성자B"); } public B(int x) { super(x); System.out.println(“매개변수생성자B" + x); public class ConstructorEx4 { public static void main(String[] args) { B b; b = new B(5); } 매개변수생성자A5 매개변수생성자B5
객체의 타입 변환 업캐스팅(upcasting) 프로그램에서 이루어지는 자동 타입 변환 서브 클래스의 레퍼런스 값을 슈퍼 클래스 레퍼런스 에 대입 슈퍼 클래스 레퍼런스가 서브 클래스 객체를 가리키게 되는 현상 객체 내에 있는 모든 멤버를 접근할 수 없고 슈퍼 클래스의 멤버만 접근 가능 class Person { } class Student extends Person { Student s = new Student(); Person p = s; // 업캐스팅, 자동타입변환
업캐스팅 사례 s p 이재문 class Person { String name; String id; public Person(String name) { this.name = name; } class Student extends Person { String grade; String department; public Student(String name) { super(name); public class UpcastingEx { public static void main(String[] args) { Person p; Student s = new Student(“이재문”); p = s; // 업캐스팅 발생 System.out.println(p.name); // 오류 없음 p.grade = “A”; // 컴파일 오류 p.department = “Com”; // 컴파일 오류 업캐스팅 사례 s p 레퍼런스 P를 이용하여서는 Student 객체의 속성 중 오직 Person 속성만 접근 가능하다. name id Person() 이재문 grade department Student() 이재문
객체의 타입 변환 다운캐스팅(downcasting) 슈퍼 클래스 레퍼런스를 서브 클래스 레퍼런스에 대입 슈퍼 클래스 레퍼런스를 서브 클래스 레퍼런스에 대입 업캐스팅된 것을 다시 원래대로 되돌리는 것 명시적으로 타입 지정 class Person { } class Student extends Person { Student s = (Student)p; // 다운캐스팅, 강제타입변환
다운캐스팅 사례 p s 이재문 public class DowncastingEx { public static void main(String[] args) { Person p = new Student(“이재문”); // 업캐스팅 발생 Student s; s = (Student)p; // 다운캐스팅 System.out.println(s.name); // 오류 없음 s.grade = “A”; // 오류 없음 } name id Person() 이재문 grade department Student() 이재문
instanceof 연산자와 객체 구별 업캐스팅된 레퍼런스로 객체의 진짜 타입을 구분하기 어려움 instanceof 연산자 하나의 슈퍼 클래스는 여러 서브 클래스에 상속된다. 서브 클래스 객체는 업캐스팅에 의해 슈퍼 클래스 레퍼런스가 가리킬 수 있다. instanceof 연산자 instanceof를 이용하여 레퍼런스가 가리키는 객체의 정확한 진짜 타입 을 식별 사용법 객체 레퍼런스 instanceof 클래스타입 --> true/false의 불린 값 class Student 업캐스팅 상속 Person p = new Student() class Person Person p가 가리키는 실제 객체는 무엇인가? class Player 상속 Person p = new Player() class Professor 상속 Person p = new Professor()
instanceof 사용 예 Person jee= new Student(); class Person { } class Student extends Person{ } class Researcher extends Person { } class Professor extends Researcher { } Person jee= new Student(); Person kim = new Professor(); Person lee = new Researcher(); if (jee instanceof Person) // jee는 Person 타입이므로 true if (jee instanceof Student) // jee는 Student 타입이므로 true if (kim instanceof Student) // kim은 Student 타입이 아니므로 false if (kim instanceof Professor) // kim은 Professor 타입이므로 true if (kim instanceof Researcher) // kim은 Researcher 타입이기도 하므로 true if (lee instanceof Professor) // lee는 Professor 타입이 아니므로 false if ("java" instanceof String) // "java"는 String 타입의 인스턴스이므로 true if (3 instanceof int) // 문법적 오류 instanceof는 객체에 대한 레퍼런스에만 사용
예제 5-3 : instanceof를 이용한 객체 구별 public class InstanceofExample { public static void main(String[] args) { Person jee= new Student(); Person kim = new Professor(); Person lee = new Researcher(); if (jee instanceof Student) // jee는 Student 타입이므로 true System.out.println("jee는 Student 타입"); if (jee instanceof Researcher) // jee는 Researcher 타입이 아니므로 false System.out.println("jee는 Researcher 타입"); if (kim instanceof Student) // kim은 Student 타입이 아니므로 false System.out.println("kim은 Student 타입"); if (kim instanceof Professor) // kim은 Professor 타입이므로 true System.out.println("kim은 Professor 타입"); if (kim instanceof Researcher) // kim은 Researcher 타입이기도 하므로 true System.out.println("kim은 Researcher 타입"); if (kim instanceof Person) // kim은 Person 타입이기도 하므로 true System.out.println("kim은 Person 타입"); if (lee instanceof Professor) // lee는 Professor 타입이 아니므로 false System.out.println("lee는 Professor 타입"); if ("java" instanceof String) // "java"는 String 타입의 인스턴스이므로 true System.out.println("\"java\"는 String 타입"); } class Person {} class Student extends Person {} class Researcher extends Person {} class Professor extends Researcher {} jee는 Student 타입 kim은 Professor 타입 kim은 Researcher 타입 kim은 Person 타입 "java"는 String 타입
메소드 오버라이딩 메소드 오버라이딩(Method Overriding) 슈퍼 클래스와 서브 클래스의 메소드에서 발생 슈퍼 클래스의 메소드를 서브 클래스에서 재정의 하는 것 슈퍼 클래스의 메소드 이름, 메소드 인자 타입 및 개수, 리턴 타입 등 모든 것 동일하게 정의 이 중 하나라도 다르면 메소드 오버라이딩 실패 슈퍼 클래스의 “메소드 무시하기”로 번역되기도 함 동적 바인딩 발생 오버라이딩된 메소드가 무조건 실행되도록 동적 바인딩 됨
메소드 재정의 메소드 재정의(method overriding): 서브 클래스 가 필요에 따라 상속된 메소드를 다시 정의하는 것
슈퍼 클래스의 메소드를 무시하고 서브 클래스에서 새로 작성한 메소드 오버라이딩 메소드1() 메소드2() 메소드3() ....... 슈퍼 클래스 메소드2() 호출 상속 서브 클래스 메소드2()
메소드 오버라이딩 사례 class DObject { public DObject next; public DObject() { next = null;} public void draw() { System.out.println(“DObject draw”); } Line, Rect, Circle 클래스눈 모두 DObject를 상속받음. class Line extends DObject { public void draw() { System.out.println(“Line”); } class Rect extends DObject { public void draw() { System.out.println(“Rect”); } class Circle extends DObject { public void draw() { System.out.println(“Circle”); }
서브 클래스 객체와 오버라이딩된 메소드 호출 (1) 서브 클래스 레퍼런스로 오버라이딩된 메소드 호출 DObject a Line a = new Line(); a.draw(); draw() draw() Line 실행 결과 : Line이 출력됨 (2) 업캐스팅에 의해 슈퍼클래스 레퍼런스로 오버라이딩된 메소드 호출(동적 바인딩) DObject p draw() DObject p = new Line(); p.draw(); 동적바인딩 draw() Line 실행 결과 : Line이 출력됨
예제 5-4 : 메소드 오버라이딩 만들기 class DObject { public class MethodOverringEx { public DObject next; public DObject() { next = null;} public void draw() { System.out.println("DObject draw"); } class Line extends DObject { public void draw() { // 메소드 오버라이딩 System.out.println("Line"); class Rect extends DObject { System.out.println("Rect"); class Circle extends DObject { System.out.println("Circle"); public class MethodOverringEx { public static void main(String[] args) { DObject obj = new DObject(); Line line = new Line(); DObject p = new Line(); DObject r = line; obj.draw(); // DObject.draw() 메소드 실행. "DObject draw" 출력 line.draw(); // Line.draw() 메소드 실행. "Line" 출력 p.draw(); // 오버라이딩된 메소드 Line.draw() 실행, "Line" 출력 r.draw(); // 오버라이딩된 메소드 Line.draw() 실행, "Line" 출력 DObject rect = new Rect(); DObject circle = new Circle(); rect.draw(); // 오버라이딩된 메소드 Rect.draw() 실행, "Rect" 출력 circle.draw(); // 오버라이딩된 메소드 Circle.draw() 실행, "Circle" 출력 } DObject draw Line Rect Circle
예제 실행 과정 DObject DObject obj “DObject draw” 출력 draw() DObject 예제 실행 과정 DObject DObject obj draw() “DObject draw” 출력 DObject Line line draw() draw() “Line” 출력 DObject r Line DObject DObject p draw() draw() “Line” 출력 Line DObject DObject rect draw() draw() “Rect” 출력 Rect DObject DObject circle draw() draw() “Circle” 출력 Circle
메소드 오버라이딩 조건 class Person { String name; String phone; static int ID; public void setName(String s) { name = s; } public String getPhone() { return phone; public static int getID() { return ID; class Professor extends Person { protected void setName(String s) { // 2번 조건위배 public String getPhone() { // 1번 조건 성공 public void getPhone(){ // 3번 조건 위배 public int getID() { // 4번 조건 위배 1. 반드시 슈퍼 클래스 메소드와 동일한 이름, 동일한 호출 인자, 반환 타입을 가 져야 한다. 2. 오버라이딩된 메소드의 접근 지정자는 슈퍼 클래스의 메소드의 접근 지정자 보 다 좁아질 수 없다. public > protected > private 순으로 지정 범위가 좁아진다. 3. 반환 타입만 다르면 오류 4. static, private, 또는 final 메소드는 오 버라이딩 될 수 없다.
오버라이딩 활용 DObject DObject DObject DObject DObject start draw() draw() public static void main(String [] args) { DObject start, n, obj; // 링크드 리스트로 도형 생성하여 연결하기 start = new Line(); //Line 객체 연결 n = start; obj = new Rect(); n.next = obj; //Rect객체 연결 n = obj; obj = new Line(); // Line 객체 연결 n.next = obj; obj = new Circle(); // Circle 객체 연결 // 모든 도형 출력하기 while(start != null) { start.draw(); start = start.next; } 오버라이딩 활용 Line Rect Circle DObject DObject DObject DObject DObject start draw() draw() draw() draw() draw() draw() draw() draw() Line Line Rect Circle
동적 바인딩 class SuperObject { protected String name; public void paint() { draw(); } public void draw() { System.out.println(“Super Object”); public class SubObject extends SuperObject { System.out.println(“Sub Object”); public static void main(String [] args) { SuperObject b = new SubObject(); b.paint(); 동적 바인딩 public class SuperObject { protected String name; public void paint() { draw(); } public void draw() { System.out.println(“Super Object”); public static void main(String [] args) { SuperObject a = new SuperObject(); a.paint(); 동적바인딩 Super Object Sub Object a b paint() draw() paint() draw() SuperObject 부분 SuperObject 부분 draw() SubObject 부분
super 키워드 super는 서브클래스에서 슈퍼 클래스의 멤버를 접근할 때 사용되는 슈퍼클래스 타입의 레퍼런스. class SuperObject { protected String name; public void paint() { draw(); } public void draw() { System.out.println(name); public class SubObject extends SuperObject { name = "Sub"; super.name = "Super"; super.draw(); public static void main(String [] args) { SuperObject b = new SubObject(); b.paint(); super는 서브클래스에서 슈퍼 클래스의 멤버를 접근할 때 사용되는 슈퍼클래스 타입의 레퍼런스. 상속관계에 있는 서브 클래스에서만 사용됨 오버라이딩된 슈퍼 클래스의 메소드 호출 시 사용 b name paint() draw() "Super" SuperObject 부분 name draw() "Sub" SubObject 부분 Super Sub
예제 5-5 : 메소드 오버라이딩 50 Person을 상속받는 Professor라는 새로운 클래스를 만들고 Professor 클래스에서 getPhone() 메소드를 재정의하라. 그리고 이 메소드에서 슈퍼 클래스의 메소드를 호출하도록 작성하라. class Person { String phone; public void setPhone(String phone) { this.phone = phone; } public String getPhone() { return phone; class Professor extends Person { return "Professor : " + super.getPhone(); public class Overriding { public static void main(String[] args) { Professor a = new Professor(); a.setPhone("011-123-1234"); System.out.println(a.getPhone()); Person p = a; System.out.println(p.getPhone()); } super.getPhone()은 아래 p.getPhone()과 달리 동적 바인딩이 일어나지 않는다. Professor : 011-123-1234 동적 바인딩에 의해 Professor의 getPhone() 호출.
오버라이딩 vs. 오버로딩
중복 정의(오버로딩) 와 재정의(오버라이딩)
추상 클래스 추상 클래스(abstract class): 몸체가 구현되지 않은 메소 드를 가지고 있는 클래스 추상 클래스는 추상적인 개념을 표현하는데 적당하다.
추상 클래스의 예
추상 클래스의 예 반드시 재정의!!!
중간 점검 문제 1. 추상 클래스의 주된 용도는 무엇인가? 주로 상속 계층에서 추상적인 개념을 나타내기 위한 용도로 사용 주로 상속 계층에서 추상적인 개념을 나타내기 위한 용도로 사용 2. 추상 클래스는 일반 메소드를 포함할 수 있는가? 추상 클래스는 일반 메소드도 포함한다 3. 추상 클래스를 상속받으면 반드시 추상 메소드 를 구현하여야 하는가? 반드시 추상 메소드를 구현해야 한다.
추상 메소드와 추상 클래스 추상 메소드(abstract method) 추상 클래스(abstract class) 선언되어 있으나 구현되어 있지 않은 메소드 추상 메소드 정의 접근 지정자 abstract 반환형 메소드이름(); ex) public abstract int getValue(); 추상 메소드는 서브 클래스에서 오버라이딩하여 구현 추상 클래스(abstract class) 추상 클래스를 하나라도 가지면 추상 클래스임 클래스 앞에 반드시 abstract라고 선언해야 함 추상 메소드가 하나도 없지만 클래스 앞에 abstract로 선언한 경우 abstract class DObject { public DObject next; public DObject() { next = null;} abstract public void draw() ; }
추상 클래스 특성 추상 클래스의 객체는 생성할 수 없다. 추상 클래스 필요성 추상 클래스의 상속 계층적 상속 관계를 갖는 클래스 구조를 만들 때 설계와 구현 분리 슈퍼 클래스에서는 개념적 특징 정의, 서브 클래스에서 구체적 행위 구현 추상 클래스의 상속 추상 클래스를 상속받아, 추상 메소드를 구현하지 않으면 서브 클래스도 추상 클래스 됨. abstract로 정의하여야 한다. 서브 클래스에서 추상 메소드를 구현하면 서브 클 래스는 추상 클래스가 되지 않는다.
2 가지 종류의 추상 클래스 동일한 컴파일 오류 발생 추상클래스는 인스턴스를 생성할 수 없음 abstract class Line { // 개발자가 임의로 추상 클래스 선언 int x; int y; public void setX(int position) { x = position; } public void setY(int position) { y = position; public int getLength() {return 0;} public class AbstractError { public static void main (String args[]) { Line l = new Line(); // 컴파일 오류 발셍 l.setX(0); l.setY(10); abstract class Line { // 추상메소드를 포함하므로 반드시 추상 클래스 int x; int y; public abstract void setX(int position); public abstract void setY(int position); public abstract int getLength(); } public class AbstractError { public static void main (String args[]) { Line l = new Line(); // 컴파일 오류 발생 l.setX(0); l.setY(10); 동일한 컴파일 오류 발생 추상클래스는 인스턴스를 생성할 수 없음
추상 클래스의 활용 예 추상 클래스로 수정 class DObject { public DObject next; public DObject() { next = null;} public void draw() { System.out.println(“DObject draw”); } abstract class DObject { public DObject next; public DObject() { next = null;} abstract public void draw(); } 추상 클래스로 수정 class Line extends DObject { public void draw() { System.out.println(“Line”); } class Rect extends DObject { public void draw() { System.out.println(“Rect”); } class Circle extends DObject { public void draw() { System.out.println(“Circle”); }
예제 5-6 : 추상 클래스의 구현 abstract class Calculator { 다음의 추상 클래스 Calculator를 상속받는 GoodCalc 클래스를 독자 임의로 작성하라. abstract class Calculator { public abstract int add(int a, int b); public abstract int subtract(int a, int b); public abstract double average(int[] a); }
예제 5-6 정답 class GoodCalc extends Calculator { public int add(int a, int b) { return a+b; } public int subtract(int a, int b) { return a - b; public double average(int[] a) { double sum = 0; for (int i = 0; i < a.length; i++) sum += a[i]; return sum/a.length; public static void main(String [] args) { Calculator c = new GoodCalc(); System.out.println(c.add(2,3)); System.out.println(c.add(new int [] {2,3,4})); 5 -1 3.0
인터페이스 : 재사용성 인터페이스(interface): 추상 메소드들로만(+ 상수) 이루 어진다. 재정의
실세계의 인터페이스와 인터페이스의 필요성
인터페이스의 필요성 인터페이스는 객체와 객체 사이의 상호 작용을 위한 인터페이스이다.
인터페이스의 예 홈 네트워킹 예제 RemoteControl
홈네트워킹 예제 인터페이스를 구현
Television 객체를 생성하여 메소드들을 호출 홈네트워킹 예제 Television 객체를 생성하여 메소드들을 호출 Refrigerator 도 동일한 방법으로 제어!
자바의 인터페이스 인터페이스(interface) 인터페이스 정의 인터페이스의 특징 모든 메소드가 추상 메소드인 클래스 인터페이스는 상수와 메소드만 갖는다. 필드는 없음 인터페이스 정의 interface 키워드로 정의된 클래스 ex) public interface SerialDriver {…} 인터페이스의 특징 메소드 선언 시 abstract 키워드를 사용하지 않아도 된다. 모든 메소드는 public으로 가정, public 접근 지정자 생략 가능 객체를 생성할 수 없음 레퍼런스 변수 타입으로 사용 가능 인터페이스의 메소드 속성 public, static, final으로 가정되므로 키워드 생략 가능
자바 인터페이스 사례 public interface Clock { public static final int ONEDAY = 24; // 상수 필드 선언 abstract public int getMinute(); abstract public int getHour(); abstract void setMinute(int i); abstract void setHour(int i); } public interface Car { int MAXIMUM_SPEED = 260; // 상수 필드 선언 int moveHandle(int degree); // abstract 생략 가능 int changeGear(int gear); // public 생략 가능
인터페이스의 필요성 인터페이스를 이용하여 다중 상속 구현 인터페이스는 명세서와 같음 클래스는 다중 상속 불가 인터페이스는 명세서와 같음 구현은 블랙 박스와 같아 인터페이스의 사용자는 구현에 대해 알 필요가 없음 인터페이스만 정의하고 구현을 분리하여, 작업자가 다양한 구현을 할 수 있 음 사용자 인터페이스 구현2 구현1 implements
인터페이스 상속 인터페이스 간에도 상속 가능 다중 상속 허용 인터페이스 상속하여 확장된 인터페이스 작성 가능 interface MobilePhone { public boolean sendCall(); public boolean receiveCall(); public boolean sendSMS(); public boolean receiveSMS(); } interface MP3 { public void play(); public void stop(); interface MusicPhone extends MobilePhone, MP3 { public void playMP3RingTone();
인터페이스 구현 인터페이스 구현 implements 키워드 사용 여러 개의 인터페이스 동시 구현 가능 상속과 구현이 동시에 가능 interface USBMouseInterface { void mouseMove(); void mouseClick(); } public class MouseDriver implements USBMouseInterface { // 인터페이스 구현. 클래스 작성 void mouseMove() { .... } void mouseClick() { ... } // 추가적으로 다른 메소드를 작성할 수 있다. int getStatus() { ... } int getButton() { ... }
인터페이스의 다중 구현 interface USBMouseInterface { void mouseMove(); void mouseClick(); } interface RollMouseInterface { void roll(); public class MouseDriver implements RollMouseInterface , USBMouseInterface { void mouseMove() { .... } void mouseClick() { ... } void roll() { ... } // 추가적으로 다른 메소드를 작성할 수 있다. int getStatus() { ... } int getButton() { ... }
여러 개의 인터페이스 구현 interface RemoteControl { // 추상 메소드 정의 public void turnON(); // 가전 제품을 켠다. public void turnOFF(); // 가전 제품을 끈다. } interface SerialCommunication { void send(byte[] data); // 시리얼 포트에 데이터를 전송한다. byte[] receive(); // 시리얼 포트에서 데이터를 받는다. public class Television implements RemoteControl, SerialCommunication { // RemoteControl과 SerialCommunication의 메소드를 동시에 구현하여야 한다. public void turnON() { } public void turnOFF() { } public void send(byte[] data) { } public byte[] receive() { return null;
인터페이스 사용 예 #1 Comparable 인터페이스 : 객체의 크기를 비교할 때 사용. public class Apartment implements Comparable { private double area = 0; public Apartment(double a) { area = a; } public int compareTo(Object otherObject) { Apartment other = (Apartment) otherObject; if (this.area < other.area) return -1; else if (this.area > other.area) return 1; else return 0; public static void main(String[] args) { Apartment o1 = new Apartment(105.3); Apartment o2 = new Apartment(85.0); if (o1.compareTo(o2) > 0) System.out.println("o1이 o2보다 더 크다"); else if (o1.compareTo(o2) < 0) System.out.println("o1이 o2보다 더 작다"); else System.out.println("o1과 o2가 같다"); Comparable 인터페이스 : 객체의 크기를 비교할 때 사용. public interface Comparable { int compareTo(Object other); } 1 : 이 객체가 다른 객체보다 클때 0 : 같을 때 -1 : 작을 때
Import java.util.Arrays; 인터페이스 사용 예 #2 public class StudentTest { public static void main(String[] args) { Student[] students = new Student[3]; students[0] = new Student("홍길동", 3.39); students[1] = new Student("임꺽정", 4.21); students[2] = new Student("황진이", 2.19); Arrays.sort(students); for (Student s : students) System.out.println("이름=" + s.getName() + “ 평점=" + s.getGPA()); } Import java.util.Arrays; Arrays 클래스 안에 정의된 정적 메소드 sort()는 대상 객체가 Comparable 인터페이스를 구현하고 있는 경우에만 동작한다. 이름=황진이 평점=2.19 이름=홍길동 평점=3.39 이름=임꺽정 평점=4.21
인터페이스 사용 예 #2 class Student implements Comparable { private String name; // 이름 private double gpa; // 평점 public Student(String n, double g) { name = n; gpa = g; } public String getName() { return name; } public double getGPA() { return gpa; } // Comparable의 추상 메소드 구현 public int compareTo(Object obj) { Student other = (Student) obj; if (gpa < other.gpa) return -1; else if (gpa > other.gpa) return 1; else return 0; 이름 순으로 정렬??? Try!!!
추상 클래스와 인터페이스 비교 인터페이스는 클래스와 비슷하지만 몇 가지 중요한 차잇점이 있다. : All methods in an interface type are abstract; they don't have an implementation All methods in an interface type are automatically public An interface type does not have instance fields
종단 클래스 와 종단 메소드 키워드 final을 붙이면 상속이나 재정의할 수 없다. final class String { … } 종단 클래스: 상속할 수 없다. 재정의할 수 없도록 한다. Cf. abstract 메소드는 반드시 재정의!
다형성 (Polymorphism) 이란? 다형성 : 코드구조를 향상하고, 가독성을 증가, 확장이 가능 동일한 부모 클래스에서 상속된 서브클래스의 객체를 하나의 타입으로 취급하게 한다. 따라서 서로 다른 타입을 받아 하나의 코드로 처리
다형성(Polymorphism) Interface variable holds reference to object of a class that implements the interface Measurable x; x = new BankAccount(10000); … x = new Coin(0.1, "dime"); Note that the object to which x refers doesn't have type Measurable; the type of the object is some class that implements the Measurable interface You can call any of the interface methods: double m = x.getMeasure(); Which method is called? x BankAccount Coin 다형성
다형성 Depends on the actual object If x refers to a bank account, calls BankAccount.getMeasure If x refers to a coin, calls Coin.getMeasure Polymorphism (many shapes): Behavior can vary depending on the actual type of an object Called late binding(지연결속): resolved at runtime Different from overloading(과적); overloading is resolved by the compiler (early binding:조기결속)
Shape 타입 변수로 Rectangle 객체를 참조하니 틀린 것 같지만 올바른 문장!! 상속과 객체 참조 Shape 타입 변수로 Rectangle 객체를 참조하니 틀린 것 같지만 올바른 문장!! Shape s = new Rectangle();// OK!
왜 그럴까? 서브 클래스 객체는 수퍼 클래스 객체를 포함하 고 있기 때문이다.
동적 바인딩 Shape의 draw()가 호출되는 것이 아니라 Rectangle의 draw()가 호출된다. s의 타입은 Shape이지만 s가 실제로 가리키고 있는 객체의 타입이 Rectangle이기 때문이다.
예제
예제
예제 어떤 draw()가 호출되는가?
다형성의 장점 만약 새로운 도형 클래스를 작성하여 추가한다고 해보 자. drawAll() 메소드는 수정할 필요가 없다.
객체의 실제 타입을 알아내는 방법 instanceof 연산자를 사용한다.
메소드의 매개 변수 메소드의 매개 변수로 수퍼 클래스 참조 변수를 이용한다. -> 다형성을 이용하는 전형적인 방법
예제
형변환 Shape s = new Rectangle(); s를 통하여 Rectangle 클래스의 필드와 메소드 를 사용하고자 할 때는 어떻게 하여야 하는가? ((Rectangle) s).setWidth(100);
과적(overloading)과 다형성(polymorphism)의 공통점과 차잇점은? Answer: Both describe a situation where one method name can denote multiple methods. However, overloading is resolved early by the compiler, by looking at the types of the parameter variables. Polymorphism is resolved late, by looking at the type of the implicit parameter object just before making the call. Big Java by Cay Horstmann Copyright © 2008 by John Wiley & Sons. All rights reserved.
내부 클래스 내부 클래스(inner class): 클래스 안에 다른 클래 스를 정의
내부 클래스의 사용 목적 특정 멤버 변수를 private로 유지하면서 자유롭 게 사용할 수 있다. 특정한 곳에서만 사용되는 클래스들을 모을 수 있다. 보다 읽기 쉽고 유지 보수가 쉬운 코드가 된다.
예제
Syntax 11.3 내부 클래스(Inner Classes) 메소드 내부에서 선언하기 class OuterClassName { method signature { . . . class InnerClassName { // methods // fields } . . . } . . . } 클래스 내부에서 선언하기 class OuterClassName { // methods // fields accessSpecifier class InnerClassName { // methods // fields } . . . } Continued
Syntax 11.3 내부 클래스(Inner Classes) 예: public class Tester { public static void main(String[] args) { class RectangleMeasurer implements Measurer { . . . } . . . } } 용도: To define an inner class whose scope is restricted to a single method or the methods of a single class.
정규 클래스 대신에 내부 클래스를 사용하는 이유는? 자습 11.11 정규 클래스 대신에 내부 클래스를 사용하는 이유는? Answer: Inner classes are convenient for insignificant classes. Also, their methods can access variables and fields from the surrounding scope. Big Java by Cay Horstmann Copyright © 2008 by John Wiley & Sons. All rights reserved.
ch11/timer/RectangleMover.java Continued 01: import java.awt.event.ActionEvent; 02: import java.awt.event.ActionListener; 03: import javax.swing.JFrame; 04: import javax.swing.Timer; 05: 06: public class RectangleMover 07: { 08: public static void main(String[] args) 09: { 10: JFrame frame = new JFrame(); 11: 12: frame.setSize(FRAME_WIDTH, FRAME_HEIGHT); 13: frame.setTitle("An animated rectangle"); 14: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15: 16: final RectangleComponent component = new RectangleComponent(); 17: frame.add(component); 18: 19: frame.setVisible(true); 20: Continued Big Java by Cay Horstmann Copyright © 2008 by John Wiley & Sons. All rights reserved.
ch11/timer/RectangleMover.java (cont.) 21: class TimerListener implements ActionListener 22: { 23: public void actionPerformed(ActionEvent event) 24: { 25: component.moveBy(1, 1); 26: } 27: } 28: 29: ActionListener listener = new TimerListener(); 30: 31: final int DELAY = 100; // Milliseconds between timer ticks 32: Timer t = new Timer(DELAY, listener); 33: t.start(); 34: } 35: 36: private static final int FRAME_WIDTH = 300; 37: private static final int FRAME_HEIGHT = 400; 38: } Big Java by Cay Horstmann Copyright © 2008 by John Wiley & Sons. All rights reserved.
LAB 1. 상속에 대한 다음 질문에 답하라. 1. 클래스 Human을 정의하여 보자. 다음과 같은 필드를 정의하라. 2. 생성자를 작성하여 보자.
LAB 1. 3. 각 필드에 대하여 접근자와 변경자를 작성하여 보자. setName(), getName(), setAge(), getAge() 4. 객체의 현재 상태를 나타내는 문자열을 반환하는 toString() 작성하여 보자. 객체의 현재 값을 반환한다. 이름 : ??? 나이 : ?? 5. 클래스 HumanTest 안에서 main() 메소드를 추가하여 서 (“춘향”,18),(“몽룡”,21),(“사또”,50)에 해당하는 Human 객체를 생성하여 보라. 각 객체의 toString을 호출해서 출력하여 보라.
LAB 1. 6. 클래스 Human을 상속하여서 Student 클래스를 작성 하여 보자. 다음과 같은 필드가 추가된다. String major; 7. 다음과 같은 생성자를 정의하여 보자. 부모 클래스의 생성자를 호출하여 보자. public Student(String name, int age, String major){ … } 8. 각 필드에 대하여 접근자와 변경자를 작성하여 보자. setMajor, getMajor
LAB 1. 9. 객체의 현재 상태를 나타내는 문자열을 반환하는 toString()을 작성하여 보자. 부모의 toString()을 호출하 여 문자열을 만들어라. 10. 클래스 HumanTest 안에서 main() 메소드 뒷부분에 (“명진”, 21, “컴퓨터공학“),(”미현“,22,”유아교육“),(”용준 “,24,”에니메이션“) 에 해당하는 Student 클래스의 객체 를 생성하여 보라. 각 객체의 toString을 호출해서 출력 하여 보라.
LAB 2. (추상클래스 사용) 1. 탈것을 나타내는 Vehicle 클래스를 추상 클래스로 정 의하여 보자. 먼저 다음과 같은 코드를 입력하자. 2. 추상 클래스에 메소드를 추가 할 수 있는지를 알아보 기 위하여 현재 속도를 문자열로 출력하는printSpeed() 메소드를 추가하여 보자. 출력 “현재 속도는”+ speed + “입니다” abstract class Vehicle { public abstract double getkilosPerLiter(); }
3. 위의 추상 클래스를 테스트하기 다음과 같은 테스트 클래스를 작성하여 보자. 3. 위의 추상 클래스를 테스트하기 다음과 같은 테스트 클래스를 작성하여 보자. 4. 추상 클래스로 객체를 생성할 수 있는지 알아보기 위 하여 ①위치에서 객체 생성 문장을 작성하고 컴파일하 여 보자. 결과를 기록하자. public class CarTest { public static void main(String args[]) { ; // ① }
5. 위의 추상 클래스를 상속받아서 Car클래스를 정의하 여 보자. 반드시 구현하여야 할 메소드는 무엇인가? // 임의의 연비 값 12.1 반환하게 할것. class Car extends Vehicle { { // (2) }
LAB 3. 인터페이스 사용 3. 다음과 같은 인터페이스를 구현하여 보자. 1. 메소드를 정의할 때 abstract를 생략하여도 되는지 실 험하여 보라. 2. 인터페이스 안에 다음과 같이 속도를 나타내는 speed 변수를 추가 할 수 있는지 실험하여 보자. 어떤 오류 메시 지가 나오는가? interface Movable { public abstract void speedUp(int amount); public abstract void speedDown(int amount); } int speed;
3. speed의 정의를 다음과 같이 변경하면 어떻게 되는가 ? int speed = 100; final int speed = 100; 상수를 나타내는 키워드인 final로 상수로 필드를 정의하였다. 인터페이스 내의 필드는 final 키워드를 사용하지 않아도 상수 취급하지만, final로 상수인 것을 명시하여 주면 더 좋다.
4. 인터페이스에 메소드를 추가할 수 있는지를 알아보기 위하여 현재 속도를 문자열로 출력하는 printSpeed()메 소드를 추가하여 보자.
speedUp(int amount) { … } speedDown(int amount) { … } 5. 인터페이스를 구현한 Car클래스를 정의하여 보자. 반 드시 구현하여야 할 메소드는 무엇인가? 필요한 필드들 과 turnLeft(), turnRight() 메소드를 추가하라. speedUp(int amount) { … } speedDown(int amount) { … } printSpeed() { … } turnLeft(), turnRight() // 간단한 메시지 출력 예) “좌회 전!” class Car ___________ Movable { … }
6. CarTest클래스의 main()에서 Car 객체를 생성하여 보 자 6. CarTest클래스의 main()에서 Car 객체를 생성하여 보 자. Car 객체를 통하여 speedUp(20), speedDown(10) 메 소드를 호출하여 보자. public class CarTest ________ Movable { public static void main(String args[]) { ; // 객체 생성 ; // 메소드 호출 }
7. Movable 참조 변수로 Car 객체를 참조하여 보라. speedUp() 메소드를 참조하여 보자 7. Movable 참조 변수로 Car 객체를 참조하여 보라. speedUp() 메소드를 참조하여 보자. turnLeft()나 turnRight() 메소드도 호출할 수 있는가? Movable m = new Car(); m.speedUp(); // 가능한가? m.turnLeft(); // 가능한가?
LAB 4. 인터페이스 사용 4. Vehicle 클래스를 상속받고 Movable 인터페이스를 구현하는 Car 클래스를 작성하여 보자. 필요한 필드들과 생성자, 메소드들을 모두 포함시켜라. 몇 개의 Car 객체 를 생성하여서 테스트 하라. <Vehicle 클래스> Vehicle.java <Movable 클래스> Movable.java <Car 클래스> Car.java <CarTest 클래스> CarTest.java class Car _____ Vehicle _____ Movable { .... // 필드, 생성자, 메소드 정의 }
Q & A