안녕! 오늘 우리는 중첩 클래스의 상속이라는 중요한 메커니즘을 살펴보겠습니다. 중첩된 클래스가 다른 클래스를 상속하도록 해야 하는 경우 수행할 작업에 대해 생각해 본 적이 있습니까? 그렇지 않다면 저를 믿으세요. 뉘앙스가 많기 때문에 이 상황은 혼란스러울 수 있습니다.
- 중첩된 클래스가 일부 클래스를 상속하도록 만들고 있습니까? 아니면 어떤 클래스가 중첩된 클래스를 상속하도록 만들고 있습니까?
- 자식/부모 클래스는 일반 공개 클래스입니까, 아니면 중첩 클래스입니까?
- 마지막으로 이러한 모든 상황에서 어떤 유형의 중첩 클래스를 사용합니까?
정적 중첩 클래스
그들의 상속 규칙은 가장 간단합니다. 여기에서 마음이 원하는 거의 모든 것을 할 수 있습니다. 정적 중첩 클래스는 다음을 상속할 수 있습니다.- 평범한 수업
- 외부 클래스 또는 그 조상에서 선언된 정적 중첩 클래스
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Drawing
코드를 변경하고 정적 중첩 클래스와 그 자손 — 을 만들어 봅시다 Boeing737Drawing
.
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
보시다시피 문제 없습니다. Drawing
우리는 클래스를 꺼내 정적 중첩 클래스 대신 일반 공개 클래스로 만들 수도 있습니다. 아무 것도 변경되지 않습니다.
public class Drawing {
}
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
우리는 이것을 이해합니다. 그러나 정적 중첩 클래스를 상속할 수 있는 클래스는 무엇입니까? 거의 모든! 중첩/비중첩, 정적/비정적 — 중요하지 않습니다. 여기에서 Boeing737Drawing
내부 클래스가 정적 중첩 클래스를 상속 하도록 합니다 Drawing
.
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public class Boeing737Drawing extends Drawing {
public int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
다음과 같은 인스턴스를 만들 수 있습니다 Boeing737Drawing
.
public class Main {
public static void main(String[] args) {
Boeing737 boeing737 = new Boeing737(1990);
Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
System.out.println(drawing.getMaxPassengersCount());
}
}
우리 Boeing737Drawing
클래스는 정적 클래스를 상속하지만 그 자체는 정적이 아닙니다! 결과적으로 항상 외부 클래스의 인스턴스가 필요합니다. Boeing737Drawing
클래스 에서 클래스를 제거 Boeing737
하고 간단한 공용 클래스로 만들 수 있습니다. 아무것도 바뀌지 않습니다. 여전히 정적 중첩 클래스를 상속할 수 있습니다 Drawing
.
public class Boeing737 {
private int manufactureYear;
public static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
}
public class Boeing737Drawing extends Boeing737.Drawing {
public int getMaxPassengersCount() {
return Boeing737.maxPassengersCount;
}
유일한 중요한 점은 이 경우 정적 변수를 공용으로 만들어야 한다는 것입니다 maxPassengersCount
. 비공개로 유지되면 일반 공개 클래스는 액세스할 수 없습니다. 우리는 정적 클래스를 알아냈습니다! :) 이제 내부 클래스로 이동하겠습니다. 단순 내부 클래스, 로컬 클래스 및 익명 내부 클래스의 세 가지 유형이 있습니다. 다시, 간단한 것에서 복잡한 것으로 이동합시다 :)
익명의 내부 클래스
익명 내부 클래스는 다른 클래스를 상속할 수 없습니다. 다른 클래스는 익명 클래스를 상속할 수 없습니다. 이보다 더 간단할 수는 없습니다! :)지역 수업
로컬 클래스(잊은 경우)는 다른 클래스의 코드 블록 내에서 선언됩니다. 대부분의 경우 이것은 외부 클래스의 일부 메서드 내에서 발생합니다. 논리적으로 동일한 메서드(또는 코드 블록) 내의 다른 로컬 클래스만 로컬 클래스를 상속할 수 있습니다. 다음은 예입니다.
public class PhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
class CellPhoneNumber extends PhoneNumber {
}
class LandlinePhoneNumber extends PhoneNumber {
}
// ...number validation code
}
}
이것은 로컬 수업에 대한 수업의 코드입니다. 번호 유효성 검사기 클래스에는 PhoneNumber
로컬 클래스가 있습니다. 예를 들어 휴대폰 번호와 유선 전화 번호와 같이 두 개의 개별 엔터티를 나타내는 데 필요한 경우 동일한 메서드 내에서만 이 작업을 수행할 수 있습니다. 그 이유는 간단합니다. 로컬 클래스의 범위는 선언된 메서드(코드 블록)로 제한됩니다. 결과적으로 외부에서 사용할 수 없습니다(클래스 상속 포함). 그러나 로컬 클래스 자체 내에서 상속 가능성은 훨씬 더 넓습니다! 로컬 클래스는 다음을 상속할 수 있습니다.
- 평범한 수업.
- 로컬 클래스와 동일한 클래스 또는 해당 조상 중 하나에서 선언된 내부 클래스입니다.
- 동일한 메서드(코드 블록)에서 선언된 다른 로컬 클래스입니다.
public class PhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
// ...number validation code
}
}
PhoneNumber
여기에서 메서드 에서 클래스를 제거 validatePhoneNumber()
하고 로컬 클래스 대신 내부 클래스로 만들었습니다. 이것은 우리의 2개의 로컬 클래스가 그것을 상속받는 것을 막지 않습니다. 예 2 — "... 또는 이 클래스의 조상에서." 이제 이것은 이미 더 흥미 롭습니다. PhoneNumber
우리는 상속 사슬에서 더 높이 올라갈 수 있습니다 . AbstractPhoneNumberValidator
클래스의 조상이 될 추상 클래스를 선언해 보겠습니다 PhoneNumberValidator
.
public abstract class AbstractPhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
}
보시다시피 선언만 한 것이 아니라 PhoneNumber
내부 클래스도 내부 클래스로 옮겼습니다. 그러나 그 후손에서 PhoneNumberValidator
메서드에 선언된 로컬 클래스는 PhoneNumber
아무 문제 없이 상속할 수 있습니다!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
// ...number validation code
}
}
상속 관계로 인해 자손 클래스 내부의 로컬 클래스는 조상 내부 클래스를 "참조"합니다. 그리고 마지막으로 마지막 그룹으로 가볼까요 :)
내부 클래스
동일한 외부 클래스(또는 그 자손)에서 선언된 내부 클래스는 다른 내부 클래스를 상속할 수 있습니다. 내부 클래스에 대한 수업의 자전거 예제를 사용하여 이를 살펴보겠습니다.
public class Bicycle {
private String model;
private int maxWeight;
public Bicycle(String model, int maxWeight) {
this.model = model;
this.maxWeight = maxWeight;
}
public void start() {
System.out.println("Let's go!");
}
class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
class SportSeat extends Seat {
// ...methods
}
}
Seat
여기서 클래스 내부에 내부 클래스를 선언했습니다 Bicycle
. 특수한 유형의 레이싱 시트인 가 SportSeat
이를 계승합니다. 그러나 별도의 "경주용 자전거" 유형을 만들어 별도의 클래스에 넣을 수 있습니다.
public class SportBicycle extends Bicycle {
public SportBicycle(String model, int maxWeight) {
super(model, maxWeight);
}
class SportSeat extends Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
이것은 또한 옵션입니다. 자손( SportBicycle.SportSeat
)의 내부 클래스는 조상의 내부 클래스를 "보고" 상속할 수 있습니다. 내부 클래스 상속에는 한 가지 매우 중요한 기능이 있습니다! 앞의 두 예제에서 우리 SportSeat
클래스는 내부 클래스였습니다. SportSeat
그러나 내부 클래스 를 동시에 상속하는 일반 공개 클래스를 만들기로 결정하면 어떻게 될까요 Seat
?
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {
public SportSeat() {
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
오류가 발생했습니다! 이유를 짐작할 수 있습니까? :) 모두 간단합니다. 내부 클래스 에 대해 이야기할 때 Bicycle.Seat
외부 클래스의 인스턴스에 대한 참조가 내부 클래스의 생성자에 암시적으로 전달된다고 언급했습니다. 즉, Seat
개체를 만들지 않고는 개체를 만들 수 없습니다 Bicycle
. 그러나 의 생성은 어떻습니까 SportSeat
? 와 달리 Seat
생성자에게 외부 클래스의 인스턴스에 대한 참조를 암시적으로 전달하는 기본 제공 메커니즘이 없습니다. 의 경우와 마찬가지로 객체가 없으면 객체를 Bicycle
생성할 수 없습니다 . 따라서 우리가 해야 할 일은 단 하나뿐입니다. 개체 에 대한 참조를 생성자 에게 명시적으로 전달하는 것입니다 . 방법은 다음과 같습니다. SportSeat
Seat
SportSeat
Bicycle
class SportSeat extends Bicycle.Seat {
public SportSeat(Bicycle bicycle) {
bicycle.super();
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Now를 사용하여 수퍼클래스 생성자를 호출합니다 super();
. 객체를 생성하려는 경우 SportSeat
아무 것도 이 작업을 중단하지 않습니다.
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
휴! 이번 수업은 다소 길었습니다 :) 하지만 많은 것을 배웠습니다! 이제 몇 가지 작업을 해결할 시간입니다! :)
GO TO FULL VERSION