개인 헬퍼 메소드가 정적 일 수있는 경우 정적이어야 함


205

인스턴스화하도록 설계된 클래스가 있다고 가정 해 봅시다. 클래스 내부에 클래스 멤버 중 하나에 액세스 할 필요가없는 몇 가지 개인 "도우미"메서드가 있으며 인수에 대해서만 작동하여 결과를 반환합니다.

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

지정하는 특별한 이유가 computeOnecomputeMore여부에 대한 특별한 이유 - 정적 방법으로는?

문제를 일으키지 않고 정적 일 수는 있지만 비 정적 상태로 두는 것이 가장 쉽습니다.


3
: 자바에서 키워드 정적 사용하지 않을 때 참조 stackoverflow.com/questions/1766715/...
마크 버틀러

답변:


174

그런 도우미 방법을 선호합니다 private static. 그러면 독자는 객체의 상태를 수정하지 않을 것입니다. 내 IDE는 정적 방법에 대한 호출을 기울임 꼴로 표시하므로 서명을 보지 않고 정적 인 방법을 알 수 있습니다.


2
내가 좋아하는 스레드 중 하나입니다 .NetBeans를 사용하고 있으며 기울임 꼴 글꼴로 정적 메서드 호출을 보여줍니다.
skiabox

2
나는 일반적으로 protected static같은 패키지의 테스트 클래스에서 매우 쉽게 테스트 할 수있는 도우미 메소드를 선언 합니다.
James

2
@James 또는 단순히 static(테스트를 위해 원한다면).
mrod

3
"객체의 상태를 수정하지 않을 것"-개인 방법과 구별하는 방법에 대한 완벽한 설명.
HopeKing 2016 년

110

정적 메소드는에 액세스 할 수 없으므로 바이트 코드가 약간 작아 질 수 있습니다 this. 속도에 차이가 있다고 생각하지 않습니다 (그렇다면 전체적으로 차이를 만들기에는 너무 작을 것입니다).

나는 일반적으로 가능하다면 그렇게하기 때문에 나는 그것들을 정적으로 만들 것이다. 그러나 그것은 단지 나입니다.


편집 : 이 답변은 바이트 코드 크기에 대한 확실한 주장으로 인해 하향 조정되고 있습니다. 실제로 테스트를 진행하겠습니다.

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

바이트 코드 (로 검색 javap -c -private TestBytecodeSize) :

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

정적 메소드를 호출하려면 두 개의 바이트 코드 (바이트)가 iconst_0필요합니다 (인수의 경우) 및 invokestatic.
비 정적 메소드를 호출하려면 aload_1( TestBytecodeSize객체의 경우), iconst_0(인수의 경우) 및 invokespecial. (이러한 개인 방법 없었다면, 그것은 참고했을 것으로 invokevirtual대신에 invokespecial, 참조 JLS §7.7 모드 실행 방법을 .)

내가 말했듯이 invokestatic바이트 코드가 하나 더 적게 필요한 것 외에는이 두 가지 성능에 큰 차이가 없을 것으로 예상 됩니다. invokestatic그리고 invokespecial모두 약간보다 더 빨리해야 invokevirtual하나가 다른 하나보다 더 빠르게 경우들이 있기 때문에 모두를 사용하는 대신 동적 바인딩 정적,하지만 난 아무 생각이 없다. 좋은 참고 자료도 찾을 수 없습니다. 내가 찾을 수있는 가장 가까운 것은 이 1997 JavaWorld 기사 이며, 기본적으로 방금 말한 내용을 다시 표시합니다.

이러한 명령어로 호출 된 메서드는 정적으로 바인딩되므로 가장 빠른 명령어는 invokespecialand invokestatic일 가능성이 높습니다 . JVM이 이러한 명령어에 대한 기호 참조를 해결하고이를 직접 참조로 바꾸면 해당 직접 참조에 실제 바이트 코드에 대한 포인터가 포함됩니다.

그러나 1997 년 이후 많은 것들이 바뀌 었습니다.

결론적으로 ... 나는 여전히 이전에 말한 것을 고수하고있는 것 같습니다. 속도는 최고의 미세 최적화이기 때문에 다른 것을 선택 해야하는 이유가 아닙니다.


매우 간단한 답변처럼 보이는 것에 대한 모든 부정적인 투표. 진실, 정의의 이익을 위해 +1 ....
Steve B.

감사. (내가 그것을 삭제하고 배지가 -3 일 때 배지를받을 수 있었지만)
Michael Myers

2
JIT 컴파일러는 어쨌든 인라인하고 최적화하므로 바이트 코드 명령의 양은 속도와 거의 관련이 없습니다.
Esko Luontola 2016 년

3
그렇기 때문에 속도에 차이가 없다고 생각합니다 (그렇다면 전체적으로 차이가 나지 않을 것입니다).
Michael Myers

7
이러한 종류의 마이크로 최적화에 참여하기 전에 핫스팟 컴파일러의 효과를 고려해야합니다. 결론 : 인간이 읽을 수 있도록 코드를 작성하고 컴파일러에 최적화를 남겨 두십시오
Ichthyo

18

개인적으로 선호하는 것은 무국적자라는 명백한 깃발이므로 정적으로 선언하는 것입니다.


18

대답은 ... 그것은 달려 있습니다.

member가 다루고있는 객체에 특정한 인스턴스 변수 인 경우 왜이를 매개 변수로 전달합니까?

예를 들어 :

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}

5
+1 : 예제가 좋지 않더라도 정적 일 수있는 너무 많은 메소드는 코드 냄새가 심합니다. 그건 그렇고, 정적 메소드는 실제로 funciton이며 전혀 OO가 아닙니다. 그것들은 다른 클래스의 메소드로 속할 수도 있고,이 함수가 메소드로 속할 수있는 클래스가 없을 수도 있습니다.
Bill K

11

당신이 "전에"클래스 생성자를 호출해야하는 경우가 정적 도우미 메서드를 선언 할 수 있습니다 이유 중 하나는 thissuper. 예를 들면 다음과 같습니다.

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

이것은 약간의 고안된 예이지만 분명히이 recoverInt경우 인스턴스 방법이 될 수는 없습니다.


생성자에서 인스턴스 메소드를 호출 할 수 있지만 super (...) 또는 this (...)를 사용하여 다른 생성자에 위임하는 경우 위임이 완료 될 때까지 인스턴스 메소드를 호출 할 수 없습니다 (하지만 정적은 알다시피 ok)
Kip

10

나는 개인 정적 메소드에 대한 분명한 장점을 생각할 수 없습니다. 즉, 정적이 아닌 것으로 만들면 특별한 이점이 없습니다. 주로 프레젠테이션의 문제입니다. 객체를 변경하지 않는다는 사실을 명확하게 강조하기 위해 정적으로 만들 수 있습니다.

다른 접근 권한을 가진 메소드의 경우 두 가지 주요 주장이 있다고 생각합니다.

  • 객체의 인스턴스를 만들지 않고 정적 메서드를 호출 할 수 있습니다.
  • 정적 메소드는 상속 될 수 없으며, 다형성이 필요한 경우 문제가 될 수 있습니다 (그러나 개인 메소드와는 관련이 없음).

게다가, 그 차이는 꽤 작으며,이 포인터가 인스턴스 메소드에 전달 한 여분의 차이가 크게 다르다는 것을 의심합니다.


4
정적이 아닌 장점은 누군가가 메소드의 동작을 서브 클래 싱하고 무시할 수 있다는 것입니다.
클린트 밀러

7
메소드를 대체 할 수 없도록 전용 메소드를 클린트하십시오.
Bhushan Bhangale

정확히, 개인 메소드의 경우 큰 차이가 없다고 생각합니다. 공공 방법을 위해, 그러나, 나는 당신이 무슨 말을하는지 동의
Axelle 지글러

8

정답은 다음과 같습니다.

필드에서 정보를 가져 오지 않고 필드에 정보를 넣지 않는 메소드는 인스턴스 메소드 일 필요는 없습니다. 클래스 나 객체에서 필드를 사용하거나 변경하지 않는 메소드는 정적 메소드 일 수도 있습니다.


5

또는 [정적으로 선언하지] 않는 특별한 이유는 무엇입니까?

예.

인스턴스 메소드로 유지하면 나중에 다른 구현을 제공 할 수 있습니다.

어리석은 소리로 들릴 수 있습니다 (실제로 이러한 메소드는 50 줄 프로그램에서만 사용됩니다). 더 큰 응용 프로그램이나 다른 사람이 사용하는 라이브러리에서는 더 나은 구현을 선택하기로 결정할 수는 있지만 그렇지 않습니다 기존 코드를 깨고 싶습니다.

따라서 서브 클래스를 작성하여 새 버전에서 리턴하고 메소드가 인스턴스 메소드로 선언되었으므로 다형성이 그 역할을 수행하게합니다.

또한 생성자를 비공개로 만들면 같은 이유로 정적 팩토리 메서드를 제공 할 수 있습니다.

따라서 권장 사항은 인스턴스 메소드로 유지하고 가능한 경우 정적을 피하는 것입니다.
언어가 제공하는 역 동성을 활용하십시오.

다소 관련된 비디오를 보려면 여기를 참조하십시오 . 좋은 API를 디자인하는 방법과 중요한 이유

"정적 대 인스턴스"메소드 토론과 직접 관련이 없지만 API 디자인에서 흥미로운 점을 다룹니다.


1
완전히 동의하십시오. 나는 보통 이런 이유로 정적과 개인을 피하려고합니다. 다른 답변의 대부분이 요점을 놓쳤다 고 생각합니다.
클린트 밀러

22
우리는 개인 도우미 에 대해 이야기하고 있습니다. 즉, 클래스의 공개 API에 속하지 않습니다. 즉, 나중에 다른 구현을 제공하여 비 정적으로 만들거나 다른 변경을 수행하는 것을 막을 수있는 것은 없습니다.
Jonik

Aaaahmm ... 좋은 지적입니다. 그럼에도 불구하고, 좋은 습관을 만드는 것은 충분한 이유가 될 것입니다 (주관적으로 말하기)
OscarRyz

2
완전히 동의하지 않습니다. 정의 된 클래스를 변경하지 않고 개인용 메소드를 변경하려는 이유는 없습니다. 제안하는 것을 수행하는 유일한 방법은 바이트 코드 조작입니다.
Peter Lawrey

2
방법이 개인이 아닌 경우 제안하는 것이 합리적이지만, 심지어는 상상의 필요성을 기반으로 디자인하는 대신 현재 필요한 전류를 기반으로 디자인하는 것이 좋습니다.
Peter Lawrey

5

정적 메서드를 사용하는 것에 대한 한 가지 문제는 개체를 단위 테스트 에 사용하기가 더 어려워 질 수 있다는 것 입니다. Mockito는 정적 메서드에 대한 모의를 만들 수 없으며 메서드의 하위 클래스 구현을 만들 수 없습니다.


2
개인 메소드에 대한 테스트를 작성해서는 안됩니다. 여기를 참조 하십시오 . 정적이든 아니든 프라이빗 메서드를 테스트 할 필요가 없습니다.
BW

@BW 그것은 매우 주관적인 것입니다. 개인용 메소드를 테스트해야하는 많은 이유가 있습니다.
ruohola

4

메소드가 기본적으로 상태 정보를 예측할 수없는 서브 루틴 인 경우 정적으로 선언하십시오.

이를 통해 다른 정적 메서드 나 클래스 초기화에 사용할 수 있습니다.

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}

3

정적 / 비 정적 질문은 "이 클래스의 객체를 정말로 사용해야합니까?"

그래서 다른 방법 사이에 객체를 전달하고 있습니까? 객체에 정적 메소드의 컨텍스트 외부에서 유용한 정보가 포함되어 있습니까? 두 가지 방법을 모두 사용할 경우 두 가지 방법을 모두 정의하지 않는 이유가 있습니까?

이 딜레마에 있다면 객체 외부의 코드에서 메소드에 필요한 모든 데이터를 가지고있는 것 같습니다. 이것이 당신이 원하는 것입니까? 매번 항상 그 데이터를 객체로 수집하는 것이 더 쉬울까요? 단일 모델에 커밋하는 것에 대해 모호 할 수 있습니다. 한 가지 방법으로 모두 할 수 있다면 정적 또는 비 정적을 선택하고 함께하십시오.


3

이와 같은 경우에 선호하는 것은 make 메소드 computeOnecomputeMore정적 메소드입니다. 이유는 캡슐화입니다. 클래스 구현에 액세스 할 수있는 코드가 적을수록 좋습니다.

당신이주는 예제에서는 것을 주장 computeOne하고 computeMore왜 내부에 참견하는 클래스의 테이너의 기회를 제공, 액세스 클래스의 내부에 필요가 없습니다.


3

다른 포스터가 잘못된 정보를 제공한다고 말한 내용을 몇 가지 명확히하고 싶습니다.

첫째, 메소드를 정적으로 선언하더라도 메소드는 비공개이므로이 클래스 외부에서는 메소드에 액세스 할 수 없습니다. 두 번째는 비공개이므로 하위 클래스에서 재정의 할 수 없으므로 정적 또는 비 정적이 아무런 차이가 없습니다. 셋째, 정적이 아닌 개인 메소드는 클래스의 생성자에서 호출 할 수도 있으며 정적 일 필요는 없습니다.

개인 헬퍼 메소드를 정적 또는 비 정적으로 정의 해야하는지 궁금한 점이 있습니다. Steve의 답변으로 개인 메서드를 정적으로 표시하면 코딩 할 때이 규칙을 따르 므로이 메서드가 상태가 없음을 표시합니다.


그러나 내 예제와 Chris Marshall의 예제와 같이 정적 메서드 만 호출 할 수있는 장소가 있습니다.
Kip

네 킵 당신과 크리스의 대답이 맞습니다. 나는 그들이 정답이기 때문에 그것에 대해 언급하지 않았습니다. 나는 오답에 대해서만 이야기했습니다.
Bhushan Bhangale

3

경험을 통해 그러한 개인 방법은 상당히 보편적이며 재사용 가능한 경향이 있다고 진술합니다.

가장 먼저 할 일은 메서드가 현재 클래스 컨텍스트 외부에서 유용 할 수 있는지 질문하는 것입니다. 그렇다면 모든 사람이 제안한 것을 정확하게 수행하고 누군가가 똑같은 일을하는 새로운 방법을 구현하기 전에 희망적으로 확인하는 일부 utils 클래스에 대해 정적으로 추출합니다.

이러한 일반적인 개인용 메소드는 프로젝트에서 코드 복제의 큰 부분을 차지합니다. 각 개발자는 자신이 사용해야하는 위치에서 독립적으로 메소드를 재발 명하기 때문입니다. 따라서 그러한 방법의 중앙 집중화는 갈 길입니다.


2

더 구체적으로 당신이 제공 한 예에서,이 메소드를 정의하는 목적은 기능을 읽는 것보다 코드를 명확하게하는 것입니다 ( 비공개 정의 됨). 이 경우 static의 목적은 클래스 기능을 노출하는 것이므로 static을 사용하면 실제로 아무런 효과가 없습니다.


다른 답변이 많이 포함되어있는이 작은 답변은 클래스 레벨 기능 대 객체 레벨 기능과 같은 문제의 본질에 실제로 영향을 미칩니다.
eljenso

고마워, 엘 정말 고마워 나는 :) 하나, 약간의 투표를 상관 없어
notnot

나는 의견을 쓸 때 +1을 주었다.
eljenso

1
동의하지 않습니다. 코드 선명도는 여전히 개인 메서드에 중요합니다. 덜 중요하지만 여전히 노력해야 할 부분입니다.
LegendLength가

1

한 가지 이유는 다른 모든 것이 같으면 정적 메소드 호출이 더 빠르기 때문입니다. 정적 메소드는 가상 일 수 없으며이 참조를 암시 적으로 사용하지 않습니다.


아래의 답변을 참조하십시오 (이미 분석이 이미 -3 인 후에 분석을 추가 했으므로 눈에 띄지 않습니다).
Michael Myers

1

많은 사람들이 말했듯이 정적 으로 만드십시오 ! 내가 따르는 엄지 손가락 규칙은 다음과 같습니다. 방법이 수학 함수 라고 생각하면 즉, 상태가없는 경우 인스턴스 변수 (=> 이클립스에 파란색 변수가 없음) 및 그 결과가 포함되지 않습니다. 메소드는 'n'번의 호출에 대해 동일하며 (물론 동일한 매개 변수를 사용) 해당 메소드를 STATIC으로 표시하십시오.

그리고이 메소드가 다른 클래스에 유용하다고 생각되면 그렇지 않으면 Util 클래스로 옮기십시오. 동일한 클래스에 메소드를 private로 넣으십시오. (접근성 최소화)


1

비 주제 : 헬퍼 메소드는 정적 메소드 만 포함 된 독립형 유틸리티 / 헬퍼 클래스에 유지합니다.

사용 시점에 도우미 메소드를 사용하는 데 따른 문제점 ( '같은 클래스'읽기)은 줄을 아는 사람이 자신의 관련이없는 도우미 메소드를 같은 장소에 게시하도록 선택할 수 있다는 것입니다


-1 : SO는 메시징 포럼이 아닙니다. 실제로 적절한 질문을하면 더 잘할 수 있습니다.
Stu Thompson

1
정적 메소드로 바인딩 / 관련된 클래스와 함께 도우미 메소드를 사용하고 싶습니다. 또한 외부에서 사용해야하는 경우 일부를 공개로 노출 할 수 있습니다. 그렇게하면 공개적으로 노출되고 집중적으로 사용된다면 해당 클래스에 대한 API를 유지하는 것이 더 쉬울 것입니다.
Ivaylo Slavov

1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

개인 정적 메소드의 장점은 클래스 변수를 다시 초기화해야하는 경우 나중에 재사용 할 수 있다는 것입니다.


1
  1. 정적 수정자가 없으면 메소드를 다시 작성할 때 쉽게 수행 할 수있는 추가 분석없이 메소드가 상태가 없음을 알 수 없습니다.

  2. 그런 다음 "정적"수정자는 다른 사람이 유용하지 않은 것 외에도 리팩토링에 대한 아이디어를 제공 할 수 있습니다. 예를 들어, 메소드를 일부 Utility 클래스로 이동하거나 멤버 메소드로 변환하십시오.


0

나는 그것들을 정적으로 선언하여 무국적자로 표시합니다.

Java는 내 보내지 않은 사소한 작업에 대한 더 나은 메커니즘을 가지고 있지 않으므로 개인 정적이 허용됩니다.

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