class 자식클래스 extends 부모클래스{}
(cf) ‘has a’ 포함관계
bank와 customer > 은행은 고객을 갖고있다 (필드를 가지고 있는것.)
> 고객은 은행이다 (상속) X
바로 자식 생성자에 숨어있다!
// DmbCellPhone의 생성자가 명시되어있지 않다면,
public DmbCellPhone() {
super();
}
// 이 코드를 컴파일러가 자동으로 생성한다.
// 만약 직접 자식생성자를 선언하고 부모생성자를 호출하고 싶다면
자식클래스(매개변수 선언,..){
super(매개값, ...); //매개값의 타입과 일치하는 부모 생성자를 호출!
...
}
🚨 부모가 먼저 생성되고 자식객체가 생성된다 >> timing 정리해보자면
main 함수에서 새로운 자식 객체가 생겨 **자식 생성자가 실행되고, 그 즉시 부모 생성자가 실행**된다. 이때 만약 자식 생성자에 부모생성자를 호출하는 super가 써있지 않다면 default 생성자를 호출하는것!
예를들어 고양이와 강아지 클래스가 있을때,
소리를 낸다/ 숨을쉰다/ 밥을먹는다 (애니멀클래스의 메소드) = 강아지와 고양이 둘 다 갖는다.
근데 소리를 낸다는 부모클래스 동작이 강아지클래스에선 멍멍, 고양이클래스에선 야옹으로 재정의 되어야한다.
// 예를들어 고양이와 강아지 클래스가 있을때,
// 소리를 낸다/ 숨을쉰다/ 밥을먹는다 (애니멀클래스의 메소드) = 강아지와 고양이 둘 다 갖는다.
// 근데 소리를 낸다는 부모클래스 동작이 강아지클래스에선 멍멍,
// 고양이클래스에선 야옹으로 재정의 되어야한다.
public class Animal {
String sound(){
return "소리를 낸다";
}
}
public class Dog extends Animal{
**// 메소드 재정의(Method Overriding)**
String sound(){
return "멍멍";
}
}
public class AnimalExample {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.sound());
}
}
오버라이딩은 덮어쓰기이다. 소리를 낸다를 멍멍으로 덮어썼기 때문에 위와같은 결과가 나온다
public class Dog extends Animal{
// 메소드 재정의(Method Overriding)
@Override
public String sound(int a){
System.out.println(super.sound());
return "멍멍";
}
public class Airplane {
public void land() {
System.out.println("착륙합니다");
}
public void fly() {
System.out.println("일반비행합니다");
}
public void tackOff() {
System.out.println("이륙합니다");
}
}
public class SupersonicAirplane extends Airplane{
public static final int NORMAL = 1;
public static final int SUPERSONIC = 2;
public int flyMode = NORMAL;
@Override
public void fly() {
if(flyMode==SUPERSONIC) {
System.out.println("초음속 비행기입니다");
} else {
super.fly();
}
}
}
public class SupersonicAirplaneExample {
public static void main(String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.tackOff(); //이륙합니다
sa.fly(); // 일반비행합니다
sa.flyMode=SupersonicAirplane.SUPERSONIC; //상수 바뀜! 상수쓸땐 클래스명.상수명 일케 쓰면 좋음 sa.상수명 ㄱㄴ하지만 경고뜸
sa.fly(); // 초음속비행기입니다
sa.flyMode=SupersonicAirplane.NORMAL; //상수바뀜
sa.fly(); //일반비행
sa.land(); //착륙합니다
}
}
Driver driver = new Driver();
Bus bus = new Bus();
보통 이런식으로 객체를 생성할텐데
맨 앞 타입 Bus는 Vehicle로 바꾸는게 좋음
Vehicle bus = new Bus();
이런식으로!
**왜냐면** 버스를 운전하려고 버스 객체를 넣어서 쓰곤 했는데
이 버스를 택시로 바꾸려면
Taxi v = new Taxi();
driver.drive(v);
이런식이니까 모든 버스 코드를 택시로 바꾸어야 함
-> 유지보수가 어렵다
: 부모와 동일하게 취급받는다!
// Animal은 부모, Cat은 자식객체
Cat cat = new Cat();
Animal animal = cat; // (형식) 부모타입 변수 = 자식타입;
합치면
Animal animal = new Cat();
// cat과 animal은 타입만 다를뿐 동일한 Cat 객체를 참조한다. (Cat주소값)
오 그렇다면 결론적으로 자동타입변환된 변수는 주소는 자식주소지만 내부에 접근 가능한 필드와 메소드는 (오버라이딩된 경우 제외하고) 부모이구나! 주소는 자식이지만 부모의 멤버를 쓴다!! new 부모를 하지 않아도 부모 멤버를 쓸수있다!
<< 이거 근데 부모 필드가 private이여도 접근 되나? ⇒ 쌉불가능
다시말해 자동타입변환이란 new로 부모를 생성하지 않아도 부모의 필드와 메소드에 접근 가능하며, 오버라이딩을 통해 메소드를 바꿀수도 있는 기능이구나!!!!
책의 예시를 해석하면, 부모인 자동차 클래스에 다양한 자식객체인 타이어클래스들이 있을때 언제든 더 좋은 타이어 클래스로 바꿀 수 있다! 차를 바꾸지 않아도!!! 차의 메소드를 오버라이딩하는 자식객체인 타이어를 더 좋은 타이어 객체로!!!!!!!!!!!!! 그럼 이건 상속/재정의/타입변환 다쓰는거네!!!!!
글고 상속은 부모+알파지만 타입변환은 오버라이딩 제외오로지 부모임ㄷㄷ
타입변환은 안써도 되는거 아닌가? ㄴㄴ car를 런 할거아님 그럼 자식이 오버라이딩 하든말든 원래 메소드가 실행됨. 근데 타입변환 쓰면 부모타입변수를 run했는데도 메소드 수정이 가능함ㄷㄷ
g헐 시발 맞음
자동타입변환은 다형성을 구현하기 위해 필요하다!
다형성의 기술적 조건
Car클래스에서 모든 타이어가 new Tire()를 이용해 필드로 선언되어있고,
특정 타이어만 Tire()로 자동타입변환하는 HankookTire()로 바꾸었다.(상속관계)
Car myCar = new Car();
myCar.backLeftTire = new HankookTire();
Car클래스의 메소드, 즉 Run()을 실행하면 내부의 backLeftTire.roll()이 실행되어야 한다.
그러나 타이어를 교체했으므로 오버라이딩 되어 HankookTire의 roll()메소드가 실행된다.
=> Car의 run()을 수정하지 않아도 다양한 roll()메소드 사용이 가능하다!
Driver 클래스에 정의된 drive() 메소드를 실행할때, 매개변수가 Vehicle 타입이다
class Driver{
void drive(Vehicle vehicle){
vehicle.run();}}
drive() 메소드를 호출은 다음과 같다
Driver driver = new Driver();
Vehicle vehicle = new Vehicle();
driver.drive(vehicle);
이때, 만약 Vehicle의 자식객체인 Bus객체를 매개값으로 넘겨준다면?
Bus bus = new Bus();
driver.drive(bus);
>> 자동타입변환 발생 <<
Vehicle vehicle = bus;
Parent parent = new Child(); // 자동타입변환
Child child = (child) parent; // 강제타입변환
자동타입변환으로 부모의 멤버만 사용 가능한 상황에서, 자식에 선언된 멤버를 꼭 사용해야 할 때 쓴다!
// Parent와 Child객체가 있을때
Parent p = new Child();
Parent는 a와 run();, Child는 c와 runA();
둘이 상속관계
주어졌을때, p.runA();나 p.c 가능할까? 둘다 불가능, Parent로 선언했기때문에 Parent만 알아
Parent p = new Child(); => Child p = new Child();로 고치면 가능
=> 결국 접근하려면, Child c = (Child)p. // c.runA();에 접근이 가능하다
boolean result = 좌항(객체) instance of 우행(타입)
// 좌항의 객체가 우항의 인스턴스라면, 즉 우행의 타입으로 객체가 생성되었다면 true를 리턴
if(parent instanceof Child) <- Child타입으로 변환 가능한지 확인
실체클래스를 여러사람이 설계할 경우 실체클래스마다 필드와 메소드가 다를 수 있다.
데이터와 기능이 동일함에도 불구하고 이름이 다르니 객체가 호출하는 이름이 달라진다. 따라서 추상클래스를 이용하여 공통기능이나 데이터를 선언해두고 상속을 통해 이름을 통일한다.
PHONE의 OWNER와 turnOn()을 cellPhone과 smartPhone에 상속하면 따로 선언할 필요가 없고,
추가적 특성인 search()와 같은 메소드만 선언하면 된다
추상클래스의 메소드는 상속받은 객체가 구현해도됨! 즉 추상클래스는 비어도 된다
public abstract class Phone {
//
public String owner;
//
public Phone(String owner) {
this.owner = owner;
}
//
public void turnOn() {
System.out.println("폰 전원을 켭니다.");
}
public void turnOff() {
System.out.println("폰 전원을 끕니다.");
}
}
public class SmartPhone extends Phone {
// 생성자
public SmartPhone(String owner) {
super(owner); //안써있으면 기본생성자가 호출된다.
}
//
public void internetSearch() {
System.out.println("인터넷 검색을 합니다");
}
}
public class PhoneExample {
public static void main(String[] args) {
// Phone phone = new Phone(null); new사용불가능!
SmartPhone smartPhone = new SmartPhone("홍길동");
//
smartPhone.turnOn();
smartPhone.internetSearch();
smartPhone.turnOff();
}
}
public abstract class Animal {
public String kind;
public void breathe() {
System.out.println("숨을 쉽니다");
}
public abstract void sound(); // {System.out.println();}
// 추상메소드는 body 적으면 안됨
}
public class Dog extends Animal {
//생성자
public Dog() {
this.kind = "포유류";
}
//추상메소드 재정의
@Override
public void sound() {
System.out.println("멍멍");
}
}
public class DogExample {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound();
System.out.println("---------");
//변수의 자동타입변환
Animal animal = null;
animal = new Dog(); //부모타입 멤버에 접근가능하다
animal.sound(); //근데 오버라이딩 돼서 Dog클래스 메소드 호출
System.out.println("---------");
//메소드의 다형성
animalSound(new Dog()); //new Dog()가 Animal animal로 들어감(자동타입변환)
}
public static void animalSound(Animal animal) {
animal.sound(); //자동타입변환된 animal객체의 sound()이므로 오버라이딩된 메소드 호출
}
}
💡 추상메서드가 있으면 반드시 추상클래스가 되어야 한다
BUT, **추상클래스라고 해서 반드시 추상메서드가 있어야 하는것은 아님!**