//상위 클래스 Car와 상속관계인 하위 클래스 FireEngine 과 Ambulance가 있다고 가정
public class Test{
public static void main(String[] args) {
Car car = new Car(); // 참조변수 타입과 객체 타입 일치 -> 가능
FireEngine fe = new FireEngine();
Car am = new Ambulance(); // 참조변수 타입과 객체 타입 불일치 -> 가능
//FireEngine fe2 = new Car(); ->하위클래스 타입으로 상위클래스 객체 참조 -> 불가능
실제 객체인 Car의 멤버 개수보다 참조변수 fe2가 사용할 수 있는 멤버 개수가 더 많기 때문에 불가능하다.
- 조건
- 서로 상속관계에 있는 상위-하위 클래스 사이에만 변환 가능
- 하위에서 상위 클래스 타입으로의 변환
(업캐스팅)
은 형변환 연산자 생략 가능
(멤버 개수가 작아지는 것은 안전)- 상위에서 하위 클래스 타입으로의 변환
(다운캐스팅)
은 형변환 연산자 반드시 명시
(멤버 개수가 많아지는 것은 안전하지 않아 명시)
//상위 클래스 Car와 상속관계인 하위 클래스 FireEngine 과 상속관계가 아닌 Ambulance가 있다고 가정
public class Test{
public static void main(String[] args) {
FireEngine fe = new FireEngine(); //
Car car = (Car) fe;//상위 클래스 Car 타입으로 변환(생략가능)
FireEngine fe2 = (FireEngine) car ; // 하위클래스 F타입으로 변환(생략 불가)
Ambulance am = (Ambulance) fe; 상속 관계가 아니므로 타입 변환 불가 -에러
boolean
타입으로 확인참조변수 instanceof 타입
public class InstanceOfExample {
public static void main(String[] args) {
Fruit fruit = new Fruit();
System.out.println(fruit instanceof Object); //true
System.out.println(fruit instanceof Fruit); //true
System.out.println(fruit instanceof Apple); //false
Fruit banana = new Banana();
System.out.println(banana instanceof Object); //true
System.out.println(banana instanceof Fruit); //true
System.out.println(banana instanceof Banana); //true
System.out.println(banana instanceof Apple); //false
}
}
class Fruit {};
class Apple extends Fruit{};
class Banana extends Fruit{};
추상 메서드
추상 클래스
abstract class AbstractExample { // 추상 메서드가 최소 하나 이상 포함된 추상 클래스
abstract void start(); // 메서드 바디가 없는 추상메서드
}
- 추상클래스를 만드는 이유
- 상속 관계에 있어 새로운 클래스를 작성하는데 유용하다.
(상위클래스는 선언부만 작성하고 실 내용은 상속을 받는 하위 클래스에서 구현)- 추상화를 구현하는데 핵심적인 역할 수행
abstract class Animal {
public String kind;
public abstract void sound();
}
class Dog extends Animal { // Animal 클래스로부터 상속
public Dog() {
this.kind = "포유류";
}
public void sound() { // 메서드 오버라이딩 -> 구현부 완성
System.out.println("멍멍");
}
}
class Cat extends Animal { // Animal 클래스로부터 상속
public Cat() {
this.kind = "포유류";
}
public void sound() { // 메서드 오버라이딩 -> 구현부 완성
System.out.println("야옹");
}
}
class DogExample {
public static void main(String[] args) throws Exception {
Animal dog = new Dog();
dog.sound();
Cat cat = new Cat();
cat.sound();
}
}
// 출력
멍멍
야옹
클래스앞에 위치 - 변경 또는 확장 불가능한 클래스, 상속 불가
메서드앞에 위치 - 오버라이딩 불가
변수 앞에 위치 - 값 변경이 불가능한 상수
final class Animal { //확장, 상속 불가능한 클래스
final int a = 1; //변경불가한 상수
final int getNum() { //오버라이딩 불가능한 메서드
final int x = a; //상수
return a;
}
}
인터페이스의 기본 구조
class
키워드 대신interface
키워드 사용- 내부의 모든 필드가
public static final
로 정의static
default
메서드 이외 모든 메서드가public abstract
로 정의
명시하지 않아도 생략 가능public interface Example { public static final int rock = 1; //인터페이스 인스턴스 변수 정의 final int scissors = 2; // public static 생략 static int paper = 3; // public , final 생략 public abstract String getPlayNum(); void call() // public abstract 생략 가능 }
인터페이스의 구현
그 자체로 인스턴스 생성x, 메서드 바디를 정의하는 클래스 따로 작성
extends
키워드 대신implements
(구현하다) 키워드 사용class 클래스명 implements 인터페이스명 { ... // 인터페이스에 정의된 모든 추상메서드 구현해야함 (강제) }
- 모든 추상 메서드들을 해당 클래스 내에서 오버라이딩하여 바디를 완성한다.
인터페이스의 다중 구현
- 인터페이스는 다중적 구현이 가능
- 하나의 클래스가 여러 개의 인터페이스를 구현 가능
- 다만 인터페이스는 인터페이스로부터만 상속이 가능하고, 최고 조상 존재x
- 애초 미완성된 멤버를 가지고 있어서 충돌 발생 여지가 없어서 다중 구현 가능
다른 클래스 상속과 동시에 인터페이스 구현 예시
인터페이스의 장점
- 역할과 구현을 분리
interface Cover { // 인터페이스 정의
public abstract void call();
}
public class Interface4 {
public static void main(String[] args) {
User user = new User();
// Provider provider = new Provider();
// user.callProvider(new Provider());
user.callProvider(new Provider1());
}
}
class User {
public void callProvider(Cover cover) { // 매개변수의 다형성 활용
cover.call();
}
}
class Provider implements Cover {
public void call() {
System.out.println("와아");
}
}
class Provider2 implements Cover {
public void call() {
System.out.println("야호");
}
} //출력 와아
인터페이스 활용 예제
객체지향개념 학습은 오늘로 끝이 났다. 각각의 개념은 어느정도 이해가 가지만 막상 코드로 작성하려고 하면 어떤식으로 시작하고 설계해야 될 지 아직 감이 안잡힌다ㅠㅠ 옵션 실습 코드를 연습해보고 싶은데 너무 어렵기만 하다. 추석 연휴에 쉬면서 실습예제를 많이 만져봐야겠다,,,