"< 액세스 수정자 (access modifiers)> 에 대해 말씀드리겠습니다 . 이전에 한 번 말씀드렸지만 반복은 학습의 기둥입니다."

다른 클래스가 클래스의 메서드 및 변수에 대해 갖는 액세스(가시성)를 제어할 수 있습니다. 액세스 수정자는 «누가 이 메소드/변수에 액세스할 수 있습니까?»라는 질문에 답합니다. 각 메소드 또는 변수에 대해 하나의 수정자만 지정할 수 있습니다.

1) « 공개 » 수식어.

public 한정자 로 표시된 변수, 메서드 또는 클래스는 프로그램의 어디에서나 액세스할 수 있습니다. 이것은 최고 수준의 개방성입니다. 제한이 없습니다.

2) « 개인 » 수식어.

private 한정자 로 표시된 변수, 메서드 또는 클래스는 선언된 클래스에서만 액세스할 수 있습니다. 표시된 메서드 또는 변수는 다른 모든 클래스에서 숨겨집니다. 이것은 가장 높은 수준의 개인 정보 보호입니다. 학급에서만 액세스할 수 있습니다. 이러한 메서드는 상속되지 않으며 재정의할 수 없습니다. 또한 하위 클래스에서 액세스할 수 없습니다.

3)  « 기본 수정자».

변수나 메서드가 수정자로 표시되지 않은 경우 "기본" 수정자로 표시된 것으로 간주됩니다. 이 한정자가 있는 변수 및 메서드는 선언된 패키지의 모든 클래스와 해당 클래스에만 표시됩니다. 이 수정자는 " 패키지 " 또는 " 패키지 전용 " 액세스 라고도 하며 변수 및 메서드에 대한 액세스가 클래스를 포함하는 전체 패키지에 대해 열려 있다는 사실을 암시합니다.

4) « 보호된 » 수정자.

이 액세스 수준은 패키지 보다 약간 더 넓습니다 . protected 한정자 로 표시된 변수, 메서드 또는 클래스는 해당 패키지(예: "패키지") 및 모든 상속된 클래스에서 액세스할 수 있습니다.

이 표는 모든 것을 설명합니다.

가시성 유형 예어 입장
당신의 수업 귀하의 패키지 후손 모든 수업
사적인 사적인 아니요 아니요 아니요
패키지 (수정자 없음) 아니요 아니요
보호 보호 아니요
공공의 공공의

이 표를 쉽게 기억할 수 있는 방법이 있습니다. 당신이 유언장을 쓰고 있다고 상상해보십시오. 모든 것을 네 가지 범주로 나누고 있습니다. 누가 당신의 물건을 사용합니까?

액세스 권한이 있는 사람 수식어
나만  _ 사적인 개인 저널
가족 (수정자 없음) 가족 사진
가족 및 상속인 보호 가족 재산
여러분 공공의 논문집

"동일한 패키지의 클래스가 한 가족의 일부라고 상상하는 것과 같습니다."

"또한 재정의 방법에 대한 몇 가지 흥미로운 뉘앙스를 알려 드리고 싶습니다."

1) 추상 메서드의 암시적 구현.

다음 코드가 있다고 가정해 보겠습니다.

암호
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

그리고 이 클래스를 상속하는 Tiger 클래스를 만들고 새 클래스에 인터페이스를 추가하기로 결정했습니다.

암호
class Cat
{
 public String getName()
 {
   return "Oscar";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

IntelliJ IDEA에서 구현하라고 지시한 누락된 메서드를 모두 구현하기만 하면 나중에 버그를 찾는 데 오랜 시간을 소비하게 될 수 있습니다.

Tiger 클래스에는 Cat에서 상속된 getName 메서드가 있으며 이는 HasName 인터페이스에 대한 getName 메서드의 구현으로 간주됩니다.

"나는 그것에 대해 끔찍한 것을 보지 않습니다."

"그렇게 나쁘지는 않습니다. 실수가 스며들기 쉬운 곳입니다."

그러나 더 나빠질 수 있습니다.

암호
interface HasWeight
{
 int getValue();
}
interface HasSize
{
 int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
 public int getValue()
 {
  return 115;
 }
}

여러 인터페이스에서 항상 상속할 수는 없는 것으로 나타났습니다. 보다 정확하게는 상속할 수는 있지만 올바르게 구현할 수는 없습니다. 예를 보십시오. 두 인터페이스 모두 getValue() 메서드를 구현해야 하지만 반환해야 하는 항목(무게 또는 크기)이 명확하지 않습니다. 이것은 처리해야 하는 것이 상당히 불쾌합니다.

"동의합니다. 메소드를 구현하고 싶지만 할 수 없습니다. 기본 클래스에서 동일한 이름을 가진 메소드를 이미 상속받았습니다. 고장났습니다."

"하지만 좋은 소식이 있습니다."

2) 가시성 확대. 유형을 상속하면 메소드의 가시성을 확장할 수 있습니다. 다음과 같이 표시됩니다.

자바 코드 설명
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
protected메서드의 가시성을 에서 로 확장했습니다 public.
암호 이것이 "합법적"인 이유
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
모든 것이 훌륭합니다. 여기서 우리는 자손 클래스에서 가시성이 확장되었다는 사실조차 알지 못합니다.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
여기에서는 가시성이 확장된 메서드를 호출합니다.

이것이 가능하지 않은 경우 Tiger에서 항상 메소드를 선언할 수 있습니다.
public String getPublicName()
{
super.getName(); //보호된 메서드 호출
}

즉, 보안 위반에 대해 말하는 것이 아닙니다.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
기본 클래스( Cat ) 에서 메서드를 호출하는 데 필요한 모든 조건이 충족되면 자손 유형( Tiger ) 에서 메서드를 호출하는 데 필요한 모든 조건이 충족됩니다 . 메서드 호출에 대한 제한이 강하지 않고 약했기 때문입니다.

"내가 완전히 이해했는지 확신할 수 없지만 이것이 가능하다는 것을 기억할 것입니다."

3) 반환 유형을 좁히십시오.

재정의된 메서드에서 반환 형식을 좁은 참조 형식으로 변경할 수 있습니다.

자바 코드 설명
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
우리는 메서드를 재정의했으며 getMyParent이제 Tiger객체를 반환합니다.
암호 이것이 "합법적"인 이유
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
모든 것이 훌륭합니다. 여기서 우리는 getMyParent 메서드의 반환 유형이 자손 클래스에서 확장되었다는 사실조차 알지 못합니다.

«오래된 코드»가 작동하고 작동하는 방식.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
여기서 반환 유형이 축소된 메서드를 호출합니다.

이것이 가능하지 않은 경우 항상 Tiger에서 메소드를 선언할 수 있습니다.
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

즉, 보안 위반 및/또는 유형 캐스팅 위반이 없습니다.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
변수의 유형을 기본 클래스(Cat)로 확장했지만 여기에서는 모든 것이 잘 작동합니다.

재정의로 인해 올바른 setMyParent 메서드가 호출됩니다.

그리고 getMyParent 메소드를 호출할 때 걱정할 것이 없습니다 . Tiger 클래스의 반환 값이 문제 없이 여전히 기본 클래스(Cat)myParent 변수에 할당 될 수 있기 때문입니다.

Tiger 개체는 Tiger 변수와 Cat 변수 모두에 안전하게 저장할 수 있습니다.

"네. 알겠습니다. 메서드를 재정의할 때 기본 클래스만 처리할 수 있고 클래스에 대해 아무것도 모르는 코드에 개체를 전달하는 경우 이 모든 것이 어떻게 작동하는지 알아야 합니다. "

"정확합니다! 그렇다면 중요한 질문은 메서드를 재정의할 때 반환 값의 유형을 좁힐 수 없는 이유는 무엇입니까?"

"이 경우 기본 클래스의 코드가 작동을 멈출 것이 분명합니다."

자바 코드 문제에 대한 설명
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
우리는 getMyParent 메서드를 오버로드하고 반환 값의 유형을 좁혔습니다.

여기 모든 것이 괜찮습니다.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
그런 다음 이 코드는 작동을 멈춥니다.

getMyParent 메소드는 실제로 Tiger 객체에서 호출되기 때문에 객체의 모든 인스턴스를 반환할 수 있습니다.

그리고 우리는 과제 전에 수표가 없습니다. 따라서 Cat 유형 myParent 변수가 String 참조를 저장하는 것이 전적으로 가능합니다.

"훌륭한 예입니다, 아미고!"

Java에서는 메서드가 호출되기 전에 개체에 해당 메서드가 있는지 여부를 확인하지 않습니다. 모든 검사는 런타임에 발생합니다. 그리고 누락된 메서드에 대한 [가상] 호출은 프로그램이 존재하지 않는 바이트코드를 실행하려고 시도하게 할 가능성이 높습니다. 이것은 궁극적으로 치명적인 오류로 이어지고 운영 체제는 프로그램을 강제로 종료합니다.

"워. 이제 알겠어."