문제

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

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) {... } 
} 

지정해야 할 특별한 이유가 있습니까? computeOne 그리고 computeMore 정적 방법으로 - 아니면 특정한 이유가 없습니까?

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

도움이 되었습니까?

해결책

나는 그러한 도우미 방법을 선호합니다 private static; 독자에게 객체의 상태를 수정하지 않을 것임을 분명히 할 것입니다. 내 IDE는 또한 이탤릭체로 정적 메소드에 대한 통화를 보여줄 것이므로 서명을 보지 않고이 방법이 정적이라는 것을 알게 될 것입니다.

다른 팁

정적 메소드가 액세스되지 않기 때문에 약간 더 작은 바이트 코드가 발생할 수 있습니다. 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.
비 정적 메소드를 호출하는 데 3 가지가 걸립니다. aload_1 ( TestBytecodeSize 대상, 나는 생각합니다), iconst_0 (논쟁을 위해), 그리고 invokespecial. (이 방법이 개인 방법이 아니었다면 invokevirtual 대신에 invokespecial; 보다 JLS §7.7 호출 방법.)

이제 내가 말했듯이, 나는이 두 가지 사이의 성능에 큰 차이가있을 것으로 기대하지 않습니다. invokestatic 바이트 코드가 하나 더 필요합니다. invokestatic 그리고 invokespecial 둘 다 조금 더 빠르야합니다 invokevirtual, 둘 다 동적 대신 정적 바인딩을 사용하기 때문에, 나는 다른 것보다 빠른지 전혀 모른다. 좋은 참조도 찾을 수 없습니다. 내가 찾을 수있는 가장 가까운 것은입니다 이 1997 년 Javaworld 기사, 기본적으로 내가 방금 말한 것을 다시 읽습니다.

가장 빠른 지침은 아마도 가능성이 높습니다 invokespecial 그리고 invokestatic, 이러한 지침에 의해 호출 된 방법은 정적으로 바인딩되기 때문입니다. JVM이 이러한 명령에 대한 상징적 참조를 해결하고이를 직접 참조로 대체 할 때, 직접 참조는 아마도 실제 바이트 코드에 대한 포인터가 포함될 것입니다.

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

결론적으로 ... 나는 아직도 내가 이전에 말한 것을 고수하고 있다고 생각합니다. 속도는 기껏해야 미세 최적화가되기 때문에 다른 하나를 선택 해야하는 이유가되어서는 안됩니다.

내 개인적인 취향은 정적을 선언하는 것입니다. 그것은 그들이 무국적으로 명확한 깃발이기 때문에 그것들을 정적으로 선언하는 것입니다.

대답은 ...에 달려 있습니다.

멤버가 다루는 객체와 관련된 인스턴스 변수 인 경우 왜 매개 변수로 전달합니까?

예를 들어:

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 */ } 
}

정적 헬퍼 방법을 선언하려는 한 가지 이유는 클래스 생성자 "이전"에서 전화를 걸어야하는 경우입니다. this 또는 super. 예를 들어:

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 이 경우 인스턴스 방법이 될 수 없습니다.

개인 정적 방법에 대한 명확한 장점을 실제로 생각할 수는 없습니다. 즉, 그것들을 비 정적으로 만들 수있는 구체적인 이점은 없습니다. 주로 프레젠테이션의 문제입니다. 객체를 변경하지 않는다는 사실을 명확하게 강조하기 위해 정적으로 만들고 싶을 수도 있습니다.

접근 권한이 다른 방법의 경우 두 가지 주요 인수가 있다고 생각합니다.

  • 객체 인스턴스를 만들지 않고도 정적 메소드를 호출 할 수 있습니다.
  • 정적 방법을 상속받을 수 없으므로 다형성이 필요한 경우 문제가 될 수 있습니다 (그러나 개인 방법에는 관련이 없습니다).

그 외에도 차이는 꽤 작으며,이 포인터가 인스턴스에 전달 된 여분의 것이 큰 차이를 만든다는 것을 의심합니다.

정답은 다음과 같습니다.

필드에서 정보를 가져 오지 않고 정보를 필드에 넣지 않는 모든 방법은 인스턴스 방법 일 필요가 없습니다. 클래스 또는 객체의 필드를 사용하거나 변경하지 않는 모든 방법은 정적 방법 일 수 있습니다.

아니면 [정적으로 선언하지 않는] 특별한 이유가 있습니까?

예.

인스턴스 방법으로 유지함으로써 나중에 다른 구현을 제공 할 수 있습니다.

어리석게 들릴 수 있습니다 (실제로 해당 방법이 50 라인 프로그램에서만 사용되는 경우)이지만 더 큰 응용 프로그램 또는 다른 사람이 사용하는 라이브러리에서는 더 나은 구현을 선택하기로 결정할 수 있지만 그렇지 않습니다. 기존 코드를 중단하고 싶습니다.

따라서 서브 클래스를 생성하고 새 버전에서 반환하고 방법이 인스턴스 방법으로 선언되었으므로 다형성이 그 일을하도록하십시오.

또한 생성자를 비공개로 만들고 같은 이유로 정적 공장 방법을 제공함으로써 이익을 얻을 수 있습니다.

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

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

"정적 대 인스턴스"메소드 토론과 직접 관련이 없지만 API 디자인의 몇 가지 흥미로운 점을 다루고 있습니다.

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

이 방법이 기본적으로 상태 정보를 예측할 수없는 서브 루틴이라면 정적으로 선언하십시오.

이를 통해 다른 정적 방법 또는 클래스 초기화에서 사용할 수 있습니다.

public class Example {
   //...

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

   //...
}

이와 같은 경우에 내가 선호하는 것은 만드는 것입니다 computeOne 그리고 computeMore 정적 방법. 이유 : 캡슐화. 클래스 구현에 액세스 할 수있는 코드가 적을수록 좋습니다.

당신이주는 예에서 당신은 그것을 말합니다 computeOne그리고 computeMore 수업의 내부에 액세스 할 필요는 없으므로 왜 클래스의 관리자가 내부와 교배 할 수있는 기회를 제공 하는가.

다른 포스터가 잘못된 정보를 제공하는 것으로 말한 몇 가지를 명확히하고 싶습니다.

첫째, 정적을 선언하더라도 메소드가 비공개 이므로이 클래스 이외의 지역에서는 액세스 할 수 없습니다. 둘째, 비공개이므로 서브 클래스에서도 무시할 수 없으므로 정적이거나 비 정적으로는 차이가 없습니다. 셋째, 비 정적 개인 방법은 클래스의 생성자에서 호출 될 수 있으며 정적 일 필요는 없습니다.

이제 개인 도우미 방법이 정적 또는 비 정적으로 정의되어야하는지 질문에옵니다. 개인 메소드를 표시하는 Steve의 답변으로 이동하여 코딩 할 때이 규칙을 따를 때이 방법이 상태가 없음을 보여줍니다.

경험에서 나는 그러한 개인 방법이 매우 보편적이고 재사용 가능한 경향이 있다고 말할 것입니다.

가장 먼저해야 할 일은이 방법이 현재 클래스 컨텍스트 외부에서 유용 할 수 있는지 여부를 묻는 것입니다. 그렇다면 모든 사람이 제안하는 것을 정확하게 수행 하고이 방법을 UTILS 클래스에 정적으로 추출하여 누군가가 정확히 같은 작업을 수행하기 전에 누군가가 희망적으로 확인합니다.

이러한 일반적인 사용 개인 방법은 프로젝트에서 코드 복제의 큰 부분의 원천이기 때문입니다. 각 개발자는 자신이 사용하는 데 필요한 장소에서 독립적으로 재창조되기 때문입니다. 따라서 그러한 방법의 중앙 집중화는 갈 수있는 방법입니다.

정적/비 정적 질문은 "이 클래스의 객체를 정말로 사용해야 할 것인가?"

그렇다면 다른 방법들 사이에서 물체를 전달하고 있습니까? 객체에는 정적 방법의 맥락 밖에서 유용한 정보가 포함되어 있습니까? 두 가지 방법을 모두 사용할 경우 두 가지 방법을 두 가지 방법으로 정의하지 않는 이유가 있습니까?

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

보다 구체적으로 주어진 예에 대해서는 이러한 메소드를 정의하는 목적이 기능보다 읽는 것보다 코드 명확성을위한 것 같습니다. ~이다 개인으로 정의). 이 경우 정적의 목적은 클래스 기능을 노출시키는 것이기 때문에 정적으로 진행하는 것은 실제로 당신에게 아무것도하지 않습니다.

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

주제 : 나는 고정 메소드만으로도 독립형 유틸리티/도우미 클래스에 도우미 방법을 유지합니다.

사용 지점에서 도우미 방법을 사용하는 데 어려움이있는 문제 ( '동일한 클래스'읽기) 라인 아래의 누군가가 같은 장소에 자신의 관련없는 도우미 방법을 게시 할 수 있다는 것입니다.

class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

개인 정적 방법의 장점은 클래스 변수를 재개 해야하는 경우 나중에 재사용 할 수 있다는 것입니다.

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

  2. 그런 다음 "정적"수정자는 다른 사람들이 불우하지 않을 수있는 다른 것들 외에 리팩토링에 대한 아이디어를 제공 할 수 있습니다. 예를 들어 메소드를 일부 유틸리티 클래스로 옮기거나 멤버 메소드로 변환합니다 ..

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

Java는 수출되지 않은 사소한 운영에 대한 더 나은 메커니즘을 가지고 있지 않으므로 개인 정적은 허용 될 수 있다고 생각합니다.

많은 사람들이 말했듯이 공전! 내가 따르는 엄지 규칙은 다음과 같습니다. 수학적 함수 즉, Instanteless이고 인스턴스 변수 (=> 메소드에서 [Eclipse에서] No Blue Color vars)가 포함되지 않으며 메소드의 결과는 'N'수의 호출에 대해 동일합니다 (동일한 매개 변수로 동일합니다. , 물론), 그 방법을 정적으로 표시하십시오.

그리고이 방법이 다른 클래스에 유용 할 것이라고 생각되면 UTIL 클래스로 이동하십시오. 그렇지 않으면 메소드를 SameClass에 비공개로 넣으십시오. (접근성 최소화)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top