메서드를 정적으로 선언하면 얻는 이점은 무엇입니까?


94

최근 Eclipse에서 내 경고를 살펴본 결과 다음과 같은 문제가 발생했습니다.

정적 경고

메서드가 정적으로 선언 될 수 있으면 컴파일러 경고를 제공합니다.

[편집] 개인 및 최종 스트레스와 이클립스 도움말 내 정확한 인용 :

활성화되면 컴파일러는 private 또는 final 이고 정적 멤버 만 참조하는 메서드에 대해 오류 또는 경고를 발행합니다 .

예, 끌 수 있다는 것을 알고 있지만 켜는 이유알고 싶습니다 .

가능한 모든 메서드를 정적으로 선언하는 것이 좋은 이유는 무엇입니까?

성능상의 이점이 있습니까? (모바일 도메인에서)

메서드를 정적으로 지적하면 인스턴스 변수를 사용하지 않으므로 utils 스타일 클래스로 이동할 수 있음을 보여줍니다.

하루가 끝날 때이 기능을 '무시'로 설정해야합니까? 아니면 100 개 이상의 경고를 수정해야합니까?

컴파일러가 어쨌든 이러한 메서드를 인라인하기 때문에 이것이 코드 를 더럽히는 추가 키워드라고 생각하십니까 ? (종료 할 수있는 모든 변수를 선언 하지는 않지만 가능합니다 .)


확실하지 않지만 이것은 단순히 프로그래밍 지원으로 볼 수 있습니다. 경고는 단순히 살펴볼 사항을 나타냅니다.
James P.

1
이 메서드가 수행하는 기능의 종류에 대해 궁금합니다. 이것들이 너무 많으면 뭔가 제대로되지 않았을 수도 있습니다.
ArjunShankar

답변:


132

메소드를 작성할 때마다 주어진 범위에서 계약을 이행합니다. 범위가 좁을수록 버그를 작성할 가능성이 줄어 듭니다.

메서드가 정적이면 비 정적 멤버에 액세스 할 수 없습니다. 따라서 범위가 더 좁습니다. 따라서 계약을 이행하기 위해 비 정적 멤버가 필요하지 않고 필요하지 않은 경우 (하위 클래스에서도) 비 정적 멤버가 왜 메서드에 이러한 필드에 대한 액세스 권한을 부여합니까? static이 경우 메서드 를 선언하면 컴파일러에서 사용하지 않을 멤버를 사용하지 않는지 확인할 수 있습니다.

또한 코드를 읽는 사람들이 계약의 성격을 이해하는 데 도움이됩니다.

이것이 static실제로 정적 계약을 구현할 때 메서드를 선언하는 것이 좋은 이유 입니다.

경우에 따라 메서드는 클래스의 인스턴스와 관련된 것을 의미 할 뿐이며 해당 구현은 실제로 비 정적 필드 또는 인스턴스를 사용하지 않습니다. 이 경우 메소드를 표시하지 않습니다 static.

static키워드를 사용하지 않을 경우의 예 :

  • 아무것도하지 않는 확장 후크 (하지만 서브 클래스의 인스턴스 데이터로 뭔가를 할 수 있음)
  • 서브 클래스에서 사용자 정의 할 수있는 매우 간단한 기본 동작입니다.
  • 이벤트 처리기 구현 : 구현은 이벤트 처리기의 클래스에 따라 다르지만 이벤트 처리기 인스턴스의 속성을 사용하지 않습니다.

1
+1 발에 자신을 쏠 기회를 최소화하고 방법을 이해하기 위해 알아야 할 정도를 줄이는 것입니다.
피터 Lawrey

7
또한 독립적이고 자체 포함 된 함수를 호출하기 위해 인스턴스를 획득하는 작업을 할 필요가 없습니다.
Marko Topolnik 2012-06-28

1
그래서 다른 세계에서는 인스턴스 변수를 사용하지 않는 메서드는 정적으로 선언해야합니까? 저는 항상 메서드가 바람직한 경우에만 정적으로 선언해야한다고 생각했습니다 (유틸리티 메서드와 같이).
페트르 Mensik

18
경우 @PetrMensik private방법은 정적을 선언 할 수 후 거의 변함이 있어야 합니다. 다른 액세스 수준의 경우 동적 디스패치와 같이 고려해야 할 다른 요소가 있습니다.
Marko Topolnik 2012-06-28

1
@JamesPoulson 내 말은 동적 메서드 디스패치, object.method()호출 할 메서드를 선택하는 동안 발생하는 일 입니다.
Marko Topolnik 2012-06-28

15

여기에는 최적화에 대한 개념이 없습니다.

static방법은 static당신이 명시 적으로 인스턴스가 필요하지 않습니다해서 둘러싸는 클래스를 의존하지 않는 방법을 선언하기 때문이다. 따라서 문서에 명시된대로 Eclipse 경고 :

활성화되면 컴파일러는 private 또는 final이고 정적 멤버 만 참조하는 메서드에 대해 오류 또는 경고를 발행합니다.

인스턴스 변수가 필요하지 않고 메서드가 비공개 (외부에서 호출 할 수 없음)이거나 최종 (재정의 할 수 없음) 인 경우 정적 메서드 대신 일반 메서드가되도록 할 이유가 없습니다. 정적 메서드는 더 적은 작업을 수행 할 수 있기 때문에 본질적으로 더 안전합니다 (인스턴스가 필요하지 않으며 암시 적 this개체가 없음).


7

성능에 대한 정보는 없지만 코드가 유형에 따라 동적 디스패치를 ​​수행 할 필요가 없기 때문에 기껏해야 약간 더 낫다고 생각합니다.

그러나 정적 메서드로 리팩토링하는 것에 대한 훨씬 더 강력한 주장은 현재 정적을 사용하는 것이 나쁜 습관으로 간주된다는 것입니다. 정적 메서드 / 변수는 객체 지향 언어에 잘 통합되지 않으며 제대로 테스트하기 어렵습니다. 이것이 일부 새로운 언어가 정적 메서드 / 변수의 개념을 완전히 포기하거나 OO (예 : 스칼라의 객체)에서 더 잘 작동하는 방식으로 언어로 내재화하려는 이유입니다.

대부분의 경우 매개 변수 만 입력으로 사용하고이를 사용하여 출력을 생성하는 함수를 구현하려면 정적 메서드가 필요합니다 (예 : 유틸리티 / 도우미 함수) 현대 언어에는이를 허용하는 첫 번째 클래스 Function 개념이 있습니다. 필요하지 않습니다. Java 8에는 람다식이 통합되어 있으므로 이미이 방향으로 이동하고 있습니다.


7
정적 메서드는 테스트하기가 쉽지 않습니다 (자체 포함되어있는 한, 특히 정적 메서드의 주요 대상인 순수 함수). OO 개념은이 경우 제공 할 것이 없습니다. 또한 일류 함수의 개념은 정적 메서드의 개념과 거의 관련이 없습니다. 기존 정적 메서드의 작업을 수행하는 일류 함수가 필요한 경우이를 구현하는 것은 소수의 문자 문제입니다.
Marko Topolnik 2012-06-28

5
테스트 가능성에 대한 언급은 아마도 정적 메서드에 직접 맞춰진 것이 아닐 수도 있지만 정적 메서드는 모의하기가 훨씬 더 어렵 기 때문에 정적 메서드를 호출하는 메서드의 테스트를 복잡하게 만듭니다.
Buhb

2
JMockit , PowerMock 또는 Groovy 와 같은 강력한 모의 프레임 워크를 사용하면 정적 메서드를 쉽게 모의 할 수 있습니다 .
Jeff Olson

이들은 private static당신이 그들을 조롱 할 필요가되지 않도록 방법
Blundell은

3

1. 신고 방법static약간의 성능 이점을 제공하지만 더 유용한 것은 객체 인스턴스가 없어도 사용할 수 있다는 것입니다 (예를 들어 팩토리 메서드 또는 싱글 톤을 가져 오는 경우를 생각해보십시오). 또한 메서드의 특성을 알려주는 문서화 목적으로도 사용됩니다. 이 문서화 목적은 코드 독자와 API 사용자에게 메소드의 본질에 대한 즉각적인 힌트를 제공하고 원래 프로그래머에게 사고의 도구 역할을하므로 무시해서는 안됩니다. 의도 된 의미에 대해 명시하면 도움이됩니다. 당신은 또한 똑바로 생각하고 더 나은 품질의 코드를 생성합니다 (나는 개인적인 경험을 기반으로 생각하지만 사람들은 다릅니다). 예를 들어, 유형에서 작동하는 메소드와 유형의 인스턴스에서 작동하는 메소드를 구별하는 것이 논리적이므로 바람직합니다.Jon Skeet의 C # 질문에 대한 의견 ).

static메서드의 또 다른 사용 사례 는 절차 적 프로그래밍 인터페이스를 모방하는 것입니다. java.lang.System.println()클래스와 그 안에있는 메서드 및 속성을 생각하십시오 . 클래스 java.lang.System는 인스턴스화 가능한 개체가 아닌 그룹화 이름 공간처럼 사용됩니다.

2. Eclipse (또는 다른 프로그래밍 된 또는 다른 종류의-생체 구성 또는 비 생체 구성-개체)는 어떤 메서드가 정적으로 선언 될 수 있는지 어떻게 알 수 있습니까? 기본 클래스가 인스턴스 변수에 액세스하지 않거나 비 정적 메서드를 호출하지 않더라도 상속 메커니즘에 의해 상황이 변경 될 수 있습니다. 하위 클래스를 상속하여 메서드를 재정의 할 수없는 경우에만 메서드가 실제로 선언 될 수 있다고 100 % 확실하게 주장 할 수 있습니다 static. 두 가지 경우에 메서드를 재정의하는 것은 정확히 불가능합니다.

  1. private (어떤 서브 클래스도 직접 사용할 수 없으며 원칙적으로도 알지 못함) 또는
  2. final (하위 클래스에서 액세스 할 수 있더라도 인스턴스 데이터 또는 함수를 참조하도록 메서드를 변경할 수있는 방법이 없습니다).

따라서 Eclipse 옵션의 논리입니다.

3. 원본 포스터는 또한 다음과 같이 묻습니다. " 메서드를 정적으로 지적하면 인스턴스 변수를 사용하지 않으므로 utils 스타일 클래스로 이동할 수 있음을 보여주고 있습니까? "이것은 매우 좋은 점입니다. 때때로 이러한 종류의 설계 변경은 경고로 표시됩니다.

Eclipse를 사용하고 Java로 프로그래밍하는 경우 개인적으로 활성화해야하는 옵션이 매우 유용합니다.


1

방법의 범위가 어떻게 변경되는지에 대한 Samuel의 답변을 참조하십시오. 이것이 메서드를 정적으로 만드는 주요 측면이라고 생각합니다.

성능에 대해서도 질문했습니다.

정적 메서드를 호출 할 때 매개 변수로 암시 적 "this"참조가 필요하지 않기 때문에 약간의 성능 향상이있을 수 있습니다.

그러나 이러한 성능 영향은 매우 작습니다. 따라서 그것은 모두 범위에 관한 것입니다.


1

Android 성능 가이드 라인에서 :

가상보다 정적 선호 객체의 필드에 액세스 할 필요가없는 경우 메서드를 정적으로 만드십시오. 호출은 약 15 % -20 % 더 빠릅니다. 또한 메서드 시그니처에서 메서드를 호출해도 개체의 상태를 변경할 수 없다는 것을 알 수 있기 때문에 좋은 방법입니다.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic


"메소드를 호출해도 객체의 상태를 변경할 수 없습니다"라는 말은 믿을 수 없을 정도로 오해의 소지가 있습니다. 나는 당신에 대해 모르지만 클래스의 정적 속성을 객체 상태의 일부로 간주합니다.
Adam Parkin

0

음, Eclipse 문서는 문제의 경고에 대해 다음과 같이 말합니다.

메서드는 정적 일 수 있습니다.

활성화되면 컴파일러는 private 또는 final이고 정적 멤버 만 참조하는 메서드에 대해 오류 또는 경고를 발행합니다.

나는 그것이 거의 모든 것을 말한다고 생각합니다. 메서드가 private이고 final이고 static 멤버 만 참조하는 경우 해당 메서드는 static으로 선언 될 수 있으며 이에 따라 정적 콘텐츠에만 액세스 할 것임을 분명히합니다.

솔직히 그 뒤에는 다른 신비한 이유가 없다고 생각합니다.


0

속도 차이에 대한 몇 가지 숫자가 누락되었습니다. 그래서 나는 그렇게 쉽지 않은 것으로 판명 된 그들을 벤치마킹하려고했습니다. 일부 실행 후 Java 루프가 느려지거나 JIT의 결함이 있습니까?

마침내 Caliper를 사용했고 결과는 손으로 테스트를 실행하는 것과 동일합니다.

정적 / 동적 호출에는 측정 가능한 차이가 없습니다. 적어도 Linux / AMD64 / Java7은 아닙니다.

Caliper 결과는 다음과 같습니다 : https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenario.vmSpec.options. CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestBlockCoalesceProximity, scenario.vmSpec.options.G1ConcMarkStepDurationMillis

내 결과는 다음과 같습니다.

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

Caliper Test 클래스는 다음과 같습니다.

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

그리고 내 자신의 테스트 클래스는 다음과 같습니다.

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

다양한 안드로이드 장치와 버전에 결과를보고 재미있을 것
Blundell은

네. 나는 당신이 관심을 가질 것이라고 생각했습니다 :) 그러나 당신은 안드로이드 소프트웨어를 만들고 있고 아마 당신의 개발 스테이션에 연결된 안드로이드 장치가 있기 때문에 나는 당신이 코드를 선택하고 실행하고 결과를 공유하는 것이 좋습니다.
Scheintod

-2

정적으로 선언 할 수있는 메서드는 다음과 같이 인스턴스화가 필요하지 않은 메서드입니다.

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

그런 다음 해당 클래스를 인스턴스화하지 않고 다른 클래스에서 반환 호출 할 수 있습니다.

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... 그러나 그것은 당신이 이미 알고있을 것입니다. 메서드가 인스턴스 변수를 사용하지 않는다는 것을 더 명확하게하는 것 외에는 그 자체로 실질적인 이점을 제공하지 않습니다.

즉, 가장 안전하게 완전히 끌 수 있습니다. 다른 클래스에서 메서드를 사용하지 않을 것임을 알고 있다면 (이 경우에는 프라이빗이어야 함) 전혀 정적 일 필요가 없습니다.


1
그 언어는 무엇입니까? 그 예제가 컴파일됩니까? 여기서 정적 인 것은 없습니다.
Piotr Perak 2012

실제로 InvertText 메서드에서 '정적'을 잊은 것 같습니다. 그리고 C #을 기반으로하는 예입니다
NeroS
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.