본문 바로가기

JAVA/JAVA 기초

가시성

반응형

자바의 변수와 메서드를 사용할 수 있는 범위를 가시성이라고 한다.

public - 모든 클래스로부터 이용할 수 있다.

protected - 서브 클래스, 동일패키지의 클래스가 이용할 수 있다.

package private (지정없음)- 동일 패키지의 클래스로부터 이용할 수 있다.

private - 자신의 클래스만 이용할 수 있다.


사용가능범위가 좁여진다.


클래스나 인터페이스에 대해서는 public, package private 중 하나만을 사용할 수 있다.

메서드와 필드는 public, protected, package private, private 사용할 수 있다.


protected 메서드는 '파생 클래스라면 다른 패키지에 속해도 참조가 가능'하다는 의미다.


또한 클래스 자신도 접근 제한자를 가질 수 있어 멤버의 접근 제한자는 클래스의 가시성에도 의존한다.

package private 클래스의 메서드나 필드에 public 접근 제한자를 지정해도 다른 패키지에서는 클래스에 액세스 할수 없기 때문에 메서드나 필드에도 액세스 할 수 없다.


//접근 제한자가 지정되어 있지 않으므로 package private 클래스

class Entity {


//멤버 자체는 public이지만 클래스가 package private

//동일 패키지의 클래스로부터만 액세스 가능하다.

public int value;

}



가시성은 넓으면 넓을수록 다른 클래스에서 사용하기 쉽다.

반대로 생각지 못한 클래스에서 호출될 가능성도 높아진다.

따라서 적절하게 설정하는 것이 중요하다.


1) 원칙적으로 가장 범위가 좁은 가시성으로 한다.

public으로 하면 다른 클래스에서 호출하거나 변수의 값을 변경할 수 있게 된다.

-클래스에서 선언하는 필드는 private으로 한다.

-외부에서 액세스하는 메서드에만 public으로 한다.


2) 확장성 높이기 위해 protected로 한다.

원칙은 private이지만, 모두 다 private 하면 사용하기 어렵게 된다.

기능확장을 하고 싶은 경우에 private 메서드로 하면 상속도 변경도 할 수 없다.

따라서 확장 가능성이 있는 메서드는 protected로 하여 미래를 위한 확장성을 높여 둔다.


XxxDataLoader 클래스의 load 메서드를 오버라이드하여 파일 이외에서도 읽을수 있도록 변경하고 싶은경우,

XxxDataLoader 클래스를 상속한 YyyDataLoader 클래스를 작성하고 load 메서드를 재정의 하려고 해도 슈퍼 클래스의 메서드가 privae이기 때문에 재정의 할 수 없어 컴파일 오류가 발생한다.

슈퍼 클래스의 load 메서드를 protected로 함으로써 컴파일 오류가 해결되어 재정의가 가능하게 된다.



public class XxxDataLoader {

private String fileName;

private List<String> load() throws IOException {

List<String> lines = Files.readAllLines(Paths.get(this.fileName));

return lines;

}

}


public class YyyDataLoader extends XxxDataLoader {

//컴파일 오류

}


private보다 protected를 사용한다.

protected는 public 만큼 가시성이 넓지 않아 의도하지 않은 곳에서 변경이 되는 위험이 적은데다 미래를 위한 확장성을 확보할 수 있기 때문이다.


3) 테스트 용이성을 높이기 위해 protected

테스트 코드를 작성할 때 테스트 대상 클래스의 필드값을 변경하거나 임의의 값을 반환하도록 메서드를 변경하고 싶은 경우,

대상 필드나 메서드가 private 라면 테스트에서 바꾸는 것이 귀찮아진다.

따라서 대상의 필드나 메서드를 protected 또는 package private로 함으로써 테스트 코드에서 쉽게 액세스하도록 하는 방법이 있다.


대상의 private필드를 package private필드로 함으로써 동일한 패키지에 배치된 테스트 코드에서 값을 덮어 쓸수 있게 되고, 시험 조건을 설정하기 위해 임이의 값으로 변경하거나 객체를 모의 객체로 교체하는 것이 용이하게 된다.

또 대상 메서드를 테스트 코드에서 호출할 수 있다. 동일한 패지에서 뿐만 아니라 상속받은 클래스에서도 조작이 가능하게 된다.


테스트용으로 모의 객체를 작성하여 메서드의 내용을 변경하거나, 모의 객체로부터 필드의 값을 읽고 쓰고 싶은 경우에는 필드나 메서드를 protected로 해두면 편리하다.


public class XxxDataLoader {

protected String fileName;

protected List<String> load() throws IOException {

List<String> lines = Files.readAllLines(Paths.get(this.fileName));

return lines;

}

}


//동일 패키지에 있게 한다.

public calss XxxDataLoaderTest {

@Test

public void testLoad() {

XxxDataLoader loader = new XxxDataLoader();

//protected의 필드나 메서드에 대해서는

// 리플렉션 등을 사용하지 않고 값을 덮어쓰거나 처리의 흐름을 실시할 수 있다.

loader.fileName ="sample.txt";

List<String> result;

try {

result = loader.load();

//...

}

//...

}

}


테스트코드로부터 private 필드나 메서드에 액세스하기 위한 방법으로 리플렉션이 있다.

리플렉션이란 클래스 구조를 읽거나 갱신할 수 있는 기능이다.

이것을 이용함으로써 private 필드의 값을 변경하거나 또는 private 메서드르 외부에서 호출할 수 있게 된다.

다만, 리플렉션은 대상 필드를 문자열로 지정하여 조작하기 때문에 소스 코드의 리팩토링에 취약해진다.


리플렉션을 사용하여 테스트를 잘해는 것도 가능하고, 그러한 목적의 라이브러리도 존재하지만, 리플렉션을 사용하는 것보다 필드나 메서드의 가시성을 넓게 하는 것이 간편하기 때문에 필드나 메서드의 가시성을 넓히는 방법을 주로 이용한다.


출처: 자바 마스터북

반응형

'JAVA > JAVA 기초' 카테고리의 다른 글

Object의 clone() 복사  (0) 2019.03.12
객체의 라이프 사이클  (0) 2018.10.25
값 전달 방법(callByValue callByReference)과 불변객체  (0) 2018.10.25
문자열관련  (0) 2018.10.22
예외 관련  (0) 2018.10.21