Java에서 보호 된 정적을 사용해서는 안되는 이유


122

이 질문을 진행 했습니다 Java에서 클래스 변수를 재정의하는 방법이 있습니까? 36 개의 찬성 투표를 한 첫 번째 댓글은 다음과 같습니다.

를 본 적이 있으면 protected static실행하십시오.

누구든지 왜 protected static눈살을 찌푸리는 지 설명 할 수 있습니까 ?


6
보호 된 정적 필드는 final. 클래스간에 공유되는 가변 정적 필드는 확실히 걱정할 필요가 있습니다. 정적 필드를 업데이트하는 여러 클래스는 신뢰할 수 없거나 따르기 쉽지 않을 것입니다. 특히 보호 된 필드 또는 메서드가 존재한다는 것은 해당 클래스가 다른 패키지의 클래스, 아마도 제어하에 있지 않은 클래스에 의해 확장된다는 것을 의미하기 때문입니다. 보호 된 필드를 포함하는 클래스의 작성자.
VGR

6
@VGR final은 필드가 변경 불가능 하다는 것을 의미하지 않습니다. 참조 변수 가 object참조하는 것을 언제든지 수정할 수 있습니다 final.
Zeeshan

@VGR 동의하지 않습니다. 정적 변수를 만드는 유일한 이유는 상속을 통해서만 다른 패키지 내에서 액세스 할 수 있기 때문이며 단일 필드에 대한 액세스가 상속의 이유가되어서는 안됩니다. 결함이있는 디자인, IMO이며, 그것에 의지한다면 아마도 애플리케이션의 구조를 재고해야 할 것입니다. 그래도 내 의견입니다.
Dioxin

@LoneRider 당신이 맞아요. 나는 불변이라고 생각하고 있었는데 최종적으로는 확실히 그것을 보장하지 않습니다.
VGR

나도 같은 질문으로 여기에 왔습니다.
Raj Rajeshwar Singh Rathore

답변:


86

직접적인 문제 라기보다는 문체에 가깝습니다. 수업에서 무슨 일이 일어나고 있는지 제대로 생각하지 못했음을 시사합니다.

static의미 에 대해 생각해보십시오 .

이 변수는 클래스 수준에 존재하며 각 인스턴스에 대해 별도로 존재 하지 않으며 나를 확장하는 클래스에 독립적으로 존재하지 않습니다 .

protected의미 에 대해 생각해보십시오 .

이 변수는이 클래스, 동일한 패키지의 클래스 및 나를 확장하는 클래스에서 볼 수 있습니다 .

두 가지 의미는 정확히 상호 배타적이지는 않지만 매우 가깝습니다.

두 가지를 함께 사용할 수있는 유일한 경우는 확장되도록 설계된 추상 클래스가 있고 확장 클래스가 원본에 정의 된 상수를 사용하여 동작을 수정할 수있는 경우입니다. 그런 종류의 배열은 대부분 매우 지저분해질 것이며 클래스 디자인의 약점을 나타냅니다.

대부분의 경우 상수를 공용으로 사용하는 것이 더 낫습니다. 그 이유는 모든 것을 더 깨끗하게 만들고 사람들이 하위 클래스를 더 유연하게 만들 수 있기 때문입니다. 대부분의 경우 구성이 상속보다 선호되는 반면 추상 클래스는 상속을 강제합니다.

이것이 어떻게 깨질 수 있는지에 대한 한 가지 예를보고 독립적 인 존재가없는 변수가 의미하는 바를 설명하려면 다음 예제 코드를 시도하십시오.

public class Program {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println(new Test2().getTest());
        Test.test = "changed";
        System.out.println(new Test2().getTest());
    }
}

abstract class Test {
    protected static String test = "test";
}

class Test2 extends Test {
    public String getTest() {
        return test;
    }
}

결과가 표시됩니다.

test
changed

https://ideone.com/KM8u8O 에서 직접 시도해보십시오.

이 클래스는 Test2정적 멤버에 액세스 할 수 있습니다 test에서 Test이름을 규정 할 필요없이 - 그러나 상속하지 않습니다 또는 자신의 사본을 얻을. 그것은 메모리에서 똑같은 물체를보고 있습니다.


4
너희들은 상속에 매달렸다. 이것이 보이는 일반적인 경우는 공개하지 않고 패키지 액세스를 원하는 사람입니다 (예 : 단위 테스트).
spudone

3
@spudone이지만 단위 테스트는 일반적으로 동일한 패키지에 배치됩니다. 액세스 권한을 부여하려면 기본 (패키지) 액세스 수준을 사용하면됩니다. 보호됨은 단위 테스트와 관련이 없거나 필요하지 않은 하위 클래스에 대한 액세스도 제공합니다.
Tim B

1
@Kamafeather Protected는 아이들이 다른 클래스에서 당신을 볼 수 있고 동일한 패키지의 모든 클래스가 당신을 볼 수 있음을 의미합니다. 예 프로그램은 동일한 패키지에 있으므로 보호 된 구성원을 볼 수 있습니다.
Tim B

2
" 확장 클래스는 동작을 수정할 수 있습니다. "이것은 Liskov Subsitution 원칙에 위배됩니다. 하위 클래스는 상위 유형의 동작을 수정해서는 안됩니다. 이는 전체 "정사각형은 직사각형이 아닙니다"인수에 속합니다. 수퍼 클래스 ( Rectangle)를 조정하여 너비 / 높이를 조정하여 동일성을 보장 Square하면 모든 수퍼 타입을 해당 하위 유형의 인스턴스로 교체하는 경우 원하지 않는 결과가 생성됩니다.
Dioxin

1
" 두 가지를 함께 사용할 수있는 유일한 경우는 확장되도록 설계된 추상 클래스가 있고 확장 클래스가 원본에 정의 된 상수를 사용하여 동작을 수정할 수있는 경우입니다. "약간 숨겨져 있지만 그곳에. 코드 예제는 또한 다음과 같은 진술을 표현합니다.
Dioxin 2016 년

32

모순적이기 때문에 눈살을 찌푸립니다.

변수를 만드는 것은 패키지 내에서protected 사용 되거나 하위 클래스 내에서 상속 된다는 것을 의미합니다 .

변수를 static만들면 클래스의 구성원이되어 상속 의도가 제거됩니다 . 이것은 패키지 내에서 사용하려는 의도만을 남겨두고 우리는 그것을 package-private위해 가지고 있습니다 (수정 자 없음).

당신은 자바 FX의 같은 응용 프로그램을 (실행하는 데 사용되어야하는 클래스를 선언한다면 나는이 유용하게 찾을 수있는 유일한 상황은 Application#launch, 단지 서브 클래스에서 발사 할 수있을 싶었다. 그렇게하면, 방법은 또한 보장 final에 허용 안 숨어 . 그러나 이것은 "규범"아니다, 아마도 응용 프로그램을 실행할 수있는 새로운 방법을 추가하여 더 복잡성을 추가하는 것을 방지하기 위해 구현되었다.

각 수정 자의 액세스 수준을 보려면 다음을 참조하십시오. The Java Tutorials-Controlling Access to Members of a Class


4
나는 static그것을 물려받는 의도를 어떻게 제거 할 수 있는지 이해하지 못한다 . 다른 패키지의 하위 클래스에 protected액세스하려면 super 필드가 여전히 필요하기 때문에 static. package-private도움이되지 수
Aobo 양에게

@AoboYang 맞아요 protected static. 그래서 어떤 사람들은 . 그러나 이것은 코드 냄새이므로 " 실행 "부분입니다. 액세스 수정 자와 상속은 서로 다른 두 가지 주제입니다. 예, 수퍼 클래스의 정적 멤버 인 경우 액세스 할 수 없습니다 package-private. 그러나 static처음에 필드 를 참조하기 위해 상속에 의존해서는 안됩니다 . 디자인이 형편 없다는 신호입니다. static메서드 를 재정의하려는 시도 가 결과를 제공하지 않음을 알 수 있습니다. 이는 상속이 클래스 기반이 아니라는 분명한 신호입니다. 클래스 또는 패키지 외부에 액세스해야하는 경우public
Dioxin 2015

3
처음에는 일부 private staticutil 함수 가있는 클래스가 있지만 누군가가 내 클래스에서 개선하거나 사용자 정의하고 싶을 수 있으며 이러한 util 함수도 그들에게 편의를 제공 할 수 있습니다. publicutil 메서드는 내 클래스의 인스턴스 사용자에게 적합하지 않을 수 있습니다. 대신 좋은 디자인을 알아낼 수 protected있습니까? 감사합니다
Aobo 양

해당 링크에는 '특정 구성원에게 적합한 가장 제한적인 액세스 수준을 사용하십시오. 타당한 이유가 없다면 비공개로 사용하십시오. ' ,이 질문과 모순되는 것은 어떤 생각입니까?
Cecilia

13

나는 이것이 왜 눈살을 찌푸려 야하는 특별한 이유를 보지 못한다. 항상 동일한 동작을 달성하기위한 대안이있을 수 있으며, 이러한 대안이 보호 된 정적 방법보다 "더 나은"지 여부는 실제 구조에 따라 달라집니다. 그러나 보호 된 정적 메서드가 합리적 일 수있는 한 가지 예는 다음과 같습니다.

( protected더 명확하게 사용하기 위해 별도의 패키지로 분할하도록 편집 )

package a;
import java.util.List;

public abstract class BaseClass
{
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeDefaultB(list);
    }

    protected static Integer computeDefaultA(List<Integer> list)
    {
        return 12;
    }
    protected static Integer computeDefaultB(List<Integer> list)
    {
        return 34;
    }
}

그로부터 파생 :

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassA extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeDefaultA(list)+computeOwnB(list);
    }

    private static Integer computeOwnB(List<Integer> list)
    {
        return 56;
    }
}

또 다른 파생 클래스 :

package a.b;

import java.util.List;

import a.BaseClass;

abstract class ExtendingClassB extends BaseClass
{
    @Override
    public Integer compute(List<Integer> list)
    {
        return computeOwnA(list)+computeDefaultB(list);
    }

    private static Integer computeOwnA(List<Integer> list)
    {
        return 78;
    }
}

protected static수정 확실히 여기 정당화 될 수 :

  • 메서드는 static인스턴스 변수에 의존하지 않기 때문에 일 수 있습니다 . 다형성 방법으로 직접 사용하기위한 것이 아니라 더 복잡한 계산의 일부인 기본 구현 을 제공 하고 실제 구현의 "구성 요소"역할을하는 "유틸리티"방법입니다 .
  • 메소드는 public구현 세부 사항이므로이 아니어야합니다 . 그리고 그들은 private확장 클래스에 의해 호출되어야하기 때문에 그럴 수 없습니다 . 또한 다른 패키지의 확장 클래스에 액세스 할 수 없기 때문에 "기본"가시성을 가질 수 없습니다.

(편집 : 원래 주석 이 메서드가 아닌 필드 만 언급했다고 가정 할 수 있지만 너무 일반적이었습니다)


이 경우 기본 구현을.가 아니라 protected final(재정의하지 않기 때문에) 로 정의해야합니다 static. 메서드가 인스턴스 변수를 사용하지 않는다고해서 반드시 static그럴 있다는 의미는 아닙니다 .
barfuin 2014-06-18

2
@Thomas 물론,이 경우에도 가능합니다. 일반적으로 : 이것은 확실히 부분적으로 주관적이지만 내 경험 법칙은 다음과 같습니다. 메서드가 다형성으로 사용되지 않고 정적 일 수있는 경우 정적으로 만듭니다. 만드는 것과는 대조적으로 final메서드가 재정의 될 의도가 없음 을 명확히 할 뿐만 아니라 메서드가 인스턴스 변수를 사용하지 않음을 독자에게 추가 로 명확하게합니다. 간단하게 말하면 정적으로 만들지 않을 이유가 없습니다 .
Marco13

1
메서드가 다형성으로 사용되도록 의도되지 않았고 정적 일 수있는 경우이를 정적으로 만듭니다. -단위 테스트를 위해 모의 프레임 워크를 사용하기 시작할 때 문제가 발생합니다. 그러나 다른 항목이 리드 우리를 ...
barfuin

@ Marco13 protected상속을 보여주지 않고 노출되지 않은 단일 패키지 아래에 유지하기 위해 무언가를 만들 때도 있습니다 . 나는 그들이 자바에있을 때 봉인 된 인터페이스가 이런 방식으로 도움이 될 것이라고 생각한다
Eugene

@Eugene 그 말은 나를 조금 짜증나게한다. 패키지 가시성은 수정 자없이 달성 할 수 있지만 protected"클래스 상속 (다른 패키지에서도)"을 의미합니다.
Marco13

7

정적 멤버는 상속되지 않으며 보호 된 멤버는 하위 클래스 (물론 포함하는 클래스)에만 표시되므로 a protected static는와 동일한 가시성 static을 가지므로 코더의 오해를 암시합니다.


1
첫 번째 진술의 출처를 제공해 주시겠습니까? 빠른 테스트에서 보호 된 static int가 문제없이 하위 클래스에 상속되고 재사용되었습니다.
hiergiltdiestfu

Protected static은 private static이 아니라 package-private static과 동일한 가시성을 갖습니다. 여기에 추가하려면 보호 및 정적을 사용하는 경우 액세스 수정자를 제거하여 패키지 전용으로 만드는 것이 가장 좋습니다 (패키지 내에서 액세스 할 수 있도록 만들려는 경우)
Dioxin

2
음 .. 아니. 패키지는 비공개이며 protected동일하지 않습니다. 라고 말하면 static필드는 동일한 패키지의 하위 클래스에만 표시됩니다.
Aaron Digulla 14

1
@AaronDigulla protected static패키지 내에서 또는 하위 클래스에서 액세스를 허용합니다 . 변수를 정적으로 만들면 서브 클래 싱의 의도가 제거되고 패키지 내에서 액세스되는 유일한 의도가 남습니다.
Dioxin

@VinceEmigh : 죄송합니다. 보헤미안과 얘기하고있었습니다. 이것을 더 명확하게해야했습니다.
아론 Digulla

5

사실 근본적으로 protected static. 패키지 및 선언 클래스의 모든 하위 클래스에 대해 표시되는 정적 변수 또는 메서드를 정말로 원하면 계속 진행하십시오 protected static.

어떤 사람들은 일반적으로 사용하지 않도록 protected여러 가지 이유로 어떤 사람들이 아닌 최종 생각 static나는의 조합을 생각 때문에 변수는, (나는 개인적으로 어느 정도 후자의 동정) 반드시 피해야 protected하고 static봐야한다 ^ 2을 나쁜 사람들이에 두 그룹에 속합니다.


3

Protected는 서브 클래스에서 사용할 수 있도록 사용됩니다. 동일한 변수에 접근 할 수있는 것은 정적 방식이므로 구체적인 클래스의 컨텍스트에서 사용할 때 보호 된 정적을 정의하는 논리는 없지만 컴파일러는 정적 방식으로 슈퍼 클래스 정적 변수에 액세스 할 때 경고를 표시합니다.


1

글쎄요, 대부분의 사람들이 대답했듯이 :

  • protected의미- ' package-private + 하위 클래스에 대한 가시성-속성 / 동작이 상 속됨 '
  • static의미- ' 인스턴스의 반대-CLASS 속성 / 동작, 즉, 상속되지 않음 '

따라서 그들은 약간 모순적이고 호환되지 않습니다.

그러나 최근에이 두 가지를 함께 사용하는 것이 합리적 일 수있는 사용 사례를 찾았습니다. 불변 유형 abstract의 부모 클래스 를 만들고 하위 유형에 공통적 인 속성을 가지고 있다고 상상해보십시오 . 불변성을 적절하게 구현 하고 가독성을 유지 하기 위해 Builder 패턴 을 사용할 수 있습니다 .

package X;
public abstract class AbstractType {
    protected Object field1;
    protected Object field2;
    ...
    protected Object fieldN;

    protected static abstract class BaseBuilder<T extends BaseBuilder<T>> {
        private Object field1; // = some default value here
        private Object field2; // = some default value here
        ...
        private Object fieldN; // = some default value here

        public T field1(Object value) { this.field1 = value; return self();}
        public T field2(Object value) { this.field2 = value; return self();}
        ...
        public T fieldN(Object value) { this.fieldN = value; return self();}
        protected abstract T self(); // should always return this;
        public abstract AbstractType build();
    }

    private AbstractType(BaseBuilder<?> b) {
        this.field1 = b.field1;
        this.field2 = b.field2;
        ...
        this.fieldN = b.fieldN;
    }
}

그리고 왜 protected static? AbstactType자체 비추 상 빌더를 구현하고 외부 package X에 위치 하여 .NET Framework에 액세스하고 재사용 할 수 있는 비추 상 하위 유형을 원 하기 때문 BaseBuilder입니다.

package Y;
public MyType1 extends AbstractType {
    private Object filedN1;

    public static class Builder extends AbstractType.BaseBuilder<Builder> {
        private Object fieldN1; // = some default value here

        public Builder fieldN1(Object value) { this.fieldN1 = value; return self();}
        @Override protected Builder self() { return this; }
        @Override public MyType build() { return new MyType(this); }
    }

    private MyType(Builder b) {
        super(b);
        this.fieldN1 = b.fieldN1;
    }
}

물론 우리는 BaseBuilder대중에게 공개 할 수 있지만 또 다른 모순 된 진술을합니다.

  • 비 인스턴트 할 수없는 클래스 (추상)가 있습니다.
  • 우리는 그것을위한 공공 건축업자를 제공합니다

그래서 모두의 경우에서 protected staticpublic의 빌더abstract class 우리는 모순 된 진술을 결합한다. 그것은 개인적인 취향의 문제입니다.

그러나, 나는 여전히 선호 public의 빌더를abstract class 때문에 protected static내게는 짐 우드와 OOP 세계에서 자연스러운 느낌!


0

가지고있는 것은 잘못된 것이 아닙니다 protected static. 많은 사람들이 간과하는 한 가지는 정상적인 상황에서 노출하고 싶지 않은 정적 메서드에 대한 테스트 케이스를 작성하고자 할 수 있다는 것입니다. 유틸리티 클래스에서 정적 메서드에 대한 테스트를 작성하는 데 특히 유용하다는 것을 알았습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.