1) implements와 @Override
-implements(인터페이스 구현)을 하면, 인터페이스에 선언된 메서드를 클래스가 반드시 구현해야 한다.
-그래서 메서드를 만들고, 구현했다는 표시로 @Override를 붙인다.
-클래스는 여러 인터페이스를 동시에 구현할 수 있다.
2) Getter / Setter
-getter: 값 읽기(조회)
-setter: 값 수정(변경)
무분별한 setter가 위험한 이유
-필드를 private로 막아놓고, 아무 검증도 없이 setter로 다 열어버리면 “접근제어” 의미가 사라진다.
-올바른 값만 들어가게 해야 한다면,
-setter를 만들지 않거나
-setter 대신 “의미 있는 변경 메서드”를 만들어서 검증 후 변경한다.
3) super
-super는 “자식이 부모의 생성자/메서드”를 호출할 때 쓴다.
-생성자에서 부모 생성자를 먼저 호출할 때 많이 사용한다.
예) super(name);
4) abstract(추상 클래스)
-abstract class: 직접 new로 객체 생성이 불가능하다.
-공통 기능(필드/일반 메서드)을 제공하면서, 일부는 자식에게 “반드시 구현”시키고 싶을 때 사용한다.
-추상 메서드(abstract method)가 있으면 자식이 반드시 override 해야 한다.
5) 업캐스팅 / 다운캐스팅
업캐스팅(자동 형변환)
-Animal animal = new Cat();
-변수 타입은 Animal(부모)로 받는 것
-이 상태에서 “컴파일러가 허용하는 호출”은 Animal에 선언된 것만 가능
다운캐스팅(강제 형변환)
-Cat cat = (Cat) animal;
-부모 타입으로 들고 있던 걸 다시 자식 타입으로 “꺼내는” 것
-이후 cat.scratch() 같은 Cat 전용 기능을 쓸 수 있다.
6) 핵심 질문 답: 업캐스팅인데 왜 animal.exist(), animal.makeSound()가 되냐?
결론
-animal이 Cat 객체인 건 맞다.
-하지만 “호출 가능한 메서드 목록”은 변수 타입(Animal)이 결정한다.
-따라서 exist(), makeSound()가 Animal(또는 Animal이 상속한 인터페이스)에 선언되어 있으면 호출 가능하다.
예시로 이해
interface LifeForm { void exist(); }
interface Animal extends LifeForm { void makeSound(); }
Cat implements Animal 이면,
-Animal 타입 변수로도 exist(), makeSound() 호출 가능
-왜? exist(), makeSound()가 Animal(부모 타입 쪽)에 이미 선언되어 있으니까
중요한 동작 원리(다형성)
-animal.makeSound()를 호출하면,
“Animal의 makeSound”를 실행하는 게 아니라
실제 객체가 Cat이므로 Cat의 오버라이딩된 makeSound()가 실행된다.
반대로 Cat 전용 메서드는 불가능
-Animal animal = new Cat();
-animal.scratch(); // 불가능 (Animal에 scratch가 없기 때문)
Cat 전용 기능이 필요하면 다운캐스팅
-if (animal instanceof Cat) {
Cat c = (Cat) animal;
c.scratch();
}
한 줄 요약
-업캐스팅 상태에서 쓸 수 있는 건 “부모 타입에 선언된 메서드”
-실행되는 건 “실제 객체(Cat)가 오버라이드한 메서드”