왜 정적 메소드가 Java에서 추상적 일 수 없습니까?


593

문제는 Java에서 추상 정적 메소드를 정의 할 수없는 이유는 무엇입니까? 예를 들어

abstract class foo {
    abstract void bar( ); // <-- this is ok
    abstract static void bar2(); //<-- this isn't why?
}

9
몇 가지 이유 : 정적 메소드는 정적 메소드에 액세스하기 위해 클래스의 인스턴스를 작성할 필요가 없으므로 추상 클래스의 일부인 경우에도 본문을 가져야합니다. 그것을 생각하는 또 다른 방법은 우리가 허용한다고 가정하면 문제는 정적 메소드 호출이 RTTI (Run Time Type Information)를 제공하지 않으며 인스턴스 생성이 필요하지 않으므로 리디렉션 할 수 없다는 것입니다 특정 재정의 된 구현에 따라 abstarct static을 허용하는 것은 전혀 의미가 없습니다. 다시 말해서, 그것은 다형성 이익을 제공 할 수 없었기 때문에 허용되지 않았다.
sactiw

6
질문에 대한 답변이 있다면, 의견이 아니라 답변 으로 게시하십시오 .
Solomon Ucko

답변:


568

"추상"은 "기능 구현 없음"을 의미하고 "정적"은 "객체 인스턴스가 없어도 기능이 있음"을 의미하기 때문입니다. 그리고 그것은 논리적 모순입니다.


353
더 간결한 대답은 '나쁜 언어 디자인'입니다. 정적은 '클래스에 속함'을 의미해야합니다. 이것이 바로이 질문에서 알 수 있듯이 직관적으로 사용되는 방식이기 때문입니다. Python의 "classmethod"를 참조하십시오.
Alexander Ljungberg

12
@Tomalak 나는 사과했다, 나는 명확하지 않았다. 물론 정적 메소드는 '클래스에 속합니다'. 여전히 동일한 네임 스페이스에 존재한다는 의미입니다. 정적 메서드는 클래스 개체 자체의 메서드가 아닙니다. 클래스 개체로 'this'와 함께 작동하지 않으며 상속 체인에 제대로 참여하지 않습니다. 그것이 진정으로 수업 방법 abstract static이라면 완벽한 의미가 될 것입니다. 서브 클래스 객체가 구현 해야하는 클래스 객체 자체의 메소드 일 것입니다. 물론, 언어에 대한 나의 이해에도 불구하고 당신의 대답이 서있는 방식은 정확합니다.
Alexander Ljungberg

695
논리적 모순이 아니며 언어 단점이며 여러 다른 언어 가이 개념을 지원합니다. "추상"은 "하위 클래스에서 구현 됨"을 의미하고, "정적"은 "클래스 인스턴스가 아닌 클래스에서 실행 됨"을 의미합니다. 논리적 모순은 없습니다.
Eric Grange

10
@Eric : 그리고 여전히 , 당신이 말하는 것은 적용되지 않습니다 abstract static: "서브 클래스에서 구현 된"함수 X 는 동시에 "클래스에서 실행될 수 없습니다 "– 서브 클래스 에서만 . 그때 더 이상 추상적이지 않은 곳.
Tomalak

72
@ Tomakak : 당신의 논리는 원형입니다. static"비어 있지 않다"는 의미는 아닙니다. 이는 정적 메소드를 추상화 할 수없는 Java의 결과입니다. "수업 가능"을 의미합니다. (이것은 " 클래스 에서만 호출 가능"을 의미해야 하지만 또 다른 문제입니다.) Java가 abstract static메소드를 지원 하는 경우 메소드 1)이 서브 클래스에 의해 구현되어야하고 2)가 서브 클래스의 클래스 메소드임을 의미합니다. 일부 메소드는 인스턴스 메소드로 의미가 없습니다. 불행히도 Java는 추상 기본 클래스 또는 인터페이스를 만들 때 지정할 수 없습니다.
Michael Carman

326

언어 디자인이 잘못되었습니다. 해당 추상 메소드를 사용하기 위해 인스턴스를 작성하는 것보다 정적 추상 메소드를 직접 호출하는 것이 훨씬 더 효과적입니다. 열거를 확장 할 수없는 해결 방법으로 추상 클래스를 사용할 때 특히 그렇습니다. 이는 또 다른 열악한 디자인 예입니다. 다음 릴리스에서 이러한 한계를 해결하기를 바랍니다.


25
Java에는 이상한 제한이 있습니다. 최종 변수에만 액세스하는 클로저는 또 다른 것입니다. 나머지 목록은 거의 끝이 없습니다. Java 프로그래머는 자신과 그 해결 방법을 알고 있어야합니다. 즐거운 시간을 보내려면 여가 시간에 Java보다 더 나은 것을 사용해야합니다. 그러나 나는 귀찮게하지 않을 것입니다. 그것이 진전을 이루는 씨앗입니다.
ceving

22
나는 당신이 "불량한 언어 디자인"이라고 부르는 것은 실제로 "보호 언어 디자인"에 가깝다고 믿습니다. 이는 불필요한 언어 기능으로 인해 프로그래머가하는 OO 원칙 위반을 제한하는 것입니다.
ethanfar

22
"추상 정적"개념이 OO 원칙을 위반합니까?
Trevor

7
@threed, 전혀는 아니지만, 물론 static그 자체 의 단순한 개념 은 이미 위반 이라고 말하는 사람들이 있습니다 ....
Pacerier

4
정적 요구는 "OO 원칙"이 일반적으로 주장하는 것만 큼 포괄적이지 않다는 명백한 증명입니다.
Ben

147

정적 메서드를 재정의 할 수 없으므로 추상적으로 만드는 것은 의미가 없습니다. 또한 추상 클래스의 정적 메서드는 재정의 클래스가 아닌 해당 클래스에 속하므로 어쨌든 사용할 수 없었습니다.


18
그렇습니다. 정적 메소드를 Java에서 재정의 할 수 없다는 것은 정말 부끄러운 일입니다.
Michel

12
@Michel : 요점은 무엇입니까? 인스턴스 기반 동작을 원하면 인스턴스 메소드를 사용하십시오.
Ran Biron

8
이 답변은 잘못되었습니다. 추상 클래스의 정적 메소드는 정상적으로 작동하며 일반적으로 사용됩니다. 단지 클래스 자체의 정적 메소드가 추상적이지 않을 수도 있습니다. @Michel 정적 메소드를 재정의하는 것은 의미가 없습니다. 인스턴스가 없으면 런타임에서 호출 할 메소드를 어떻게 알 수 있습니까?
erickson

63
@erickson-인스턴스가 없어도 클래스 계층 구조는 손상되지 않습니다. 정적 메서드의 상속은 인스턴스 메서드의 상속과 동일하게 작동 할 수 있습니다. 스몰 토크가이를 수행하며 매우 유용합니다.
Jared

8
@matiasg 전혀 새로운 것이 아닙니다. 추상 클래스에는 항상 정적이고 비 추상적 인 메소드 가 허용되었습니다 .
매트 볼

70

abstract메소드에 대한 주석은 메소드가 서브 클래스에서 대체되어야 함을 나타냅니다.

자바에서는 static멤버 (메소드 나 필드) A (이것은 다른 객체 지향 언어로, 스몰 토크를 참조하십시오. 반드시 사실이 아니다) 서브 클래스에 의해 오버라이드 (override) 할 수없는 static구성원이 될 수 있습니다 숨겨져 있지만,보다 근본적으로 다르다 오버라이드 (override) .

정적 멤버는 서브 클래스에서 재정의 abstract할 수 없으므로 주석을 적용 할 수 없습니다.

다른 언어는 인스턴스 상속과 마찬가지로 정적 상속도 지원합니다. 구문 관점에서 이러한 언어는 일반적으로 명령문에 클래스 이름을 포함해야합니다. 예를 들어, Java에서 ClassA로 코드를 작성한다고 가정하면 다음과 같은 명령문입니다 (methodA ()가 정적 메소드이고 동일한 서명을 가진 인스턴스 메소드가없는 경우).

ClassA.methodA();

methodA();

SmallTalk에서 클래스 이름은 선택 사항이 아니므로 구문은 다음과 같습니다 (SmallTalk는.를 사용하여 "제목"과 "동사"를 구분하지 않고 대신이를 상태 수정 종료 자로 사용합니다).

ClassA methodA.

클래스 이름은 항상 필요하므로 클래스 계층 구조를 통과하여 메소드의 올바른 "버전"을 항상 결정할 수 있습니다. 그 가치가 있기 때문에 때로는 static상속이 누락 되고 처음 시작할 때 Java에서 정적 상속이 부족하여 물 렸습니다. 또한 SmallTalk는 오리 형식이므로 계약별로 지원하지 않습니다. 따라서 abstract클래스 멤버를위한 수정자가 없습니다 .


1
"하위 클래스가 정적 멤버를 대체 할 수 없습니다"가 잘못되었습니다. 적어도 Java6에서는 가능합니다. 언제인지 확실하지 않습니다.
Steven De Groote

15
@Steven De Groote 정적 멤버는 실제로 서브 클래스로 대체 될 수 없습니다. 서브 클래스에 슈퍼 클래스의 정적 메소드와 동일한 서명이있는 정적 메소드가 있으면이를 대체하지 않고 숨 깁니다. http://docs.oracle.com/javase/tutorial/java/IandI/override.html 차이점은 다형성은 재정의 된 경우에만 적용되지만 숨겨진 메서드에는 적용되지 않는다는 것입니다.
John29

2
@ John29 설명을 해주셔서 감사합니다. 그러나 이름 차이를 제외하고는 사용법이 비슷해 보입니다.
Steven De Groote

2
@Steven De Groote 예, 사용법은 비슷하지만 동작이 다릅니다. 이것이 정적 추상 메소드가없는 이유입니다. 다형성을 지원하지 않는 정적 추상 메소드의 요점은 무엇입니까?
John29

3
@Steven De Groote : 수퍼 클래스 자체에서 해당 메소드를 호출하면 차이점이 분명해집니다. Super.foo가 Super.bar를 호출한다고 가정하십시오. 서브 클래스가 Subclass.bar를 구현 한 다음 foo를 호출하면 foo는 여전히 Subclass.bar가 아닌 Super.bar를 호출합니다. 따라서, 당신이 실제로 가지고있는 것은 "bar"라고 불리는 완전히 다른 두 가지 관련이없는 방법입니다. 이것은 유용한 의미로 무시되지 않습니다.
Doradus September

14

나는 또한 같은 질문을했다, 여기에 이유가있다.

Abstract 클래스가 말 했으므로 구현을 제공하지 않고 서브 클래스가 제공 할 수 있습니다

서브 클래스는 Superclass의 메소드를 재정의해야합니다.

규칙 NO 1 - 정적 메서드는 재정의 할 수 없습니다

정적 멤버와 메서드는 컴파일 타임 요소이므로 정적 메서드의 오버로드 (컴파일 타임 다형성)가 재정의 (런타임 다형성)보다 허용되는 이유

그래서 그들은 추상적 일 수 없습니다.

추상 정적 <--- 와 같은 것은 없습니다 . Java Universe에서는 허용되지 않습니다.


5
-1, "정적 멤버 및 메소드가 컴파일 시간 요소이므로 Java가 정적 메소드를 대체 할 수 없습니다"는 사실이 아닙니다. 정적 유형 검사는 stackoverflow.com/questions/370962/…abstract static참조 하면 확실히 가능합니다 . 진짜 이유 자바는 정적 메소드가 오버라이드 (override) 할 수 없기 때문에 자바는 정적 메소드 오버라이드 (override) 할 수 허용하지 않는 이유입니다.
Pacerier

과부하는 다형성과 관련이 없습니다. 오버로드 및 재정의는 "over"라는 접두사를 제외하고는 공통점이 없습니다. Java 및 JavaScript가 "Java"를 갖는 것과 거의 동일한 방식입니다. 메소드의 이름은 그것이 무엇인지 식별하는 것이 아니라 서명의 특징입니다. 그래서 그것 foo(String)과 같지 않습니다 foo(Integer)– 그것이 전부입니다.
Captain Man

@CaptainMan Overloading은 말 그대로 "매개 변수 다형성"이라고합니다. 매개 변수의 유형에 따라 다른 방법 인 다형성이라는 메소드가 호출되기 때문입니다.
Davor

12

이것은 끔찍한 언어 디자인이며 실제로 불가능한 이유는 없습니다.

실제로 다음은 JAVA 에서 수행 할 있는 방법에 대한 구현입니다 .

public class Main {

        public static void main(String[] args) {
                // This is done once in your application, usually at startup
                Request.setRequest(new RequestImplementationOther());

                Request.doSomething();
        }

        public static final class RequestImplementationDefault extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something AAAAAA");
                }
        }

        public static final class RequestImplementaionOther extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something BBBBBB");
                }
        }

        // Static methods in here can be overriden
        public static abstract class Request {

                abstract void doSomethingImpl();

                // Static method
                public static void doSomething() {
                        getRequest().doSomethingImpl();
                }

                private static Request request;
                private static Request getRequest() {
                        // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. 
                        if ( request == null ) {
                                return request = new RequestImplementationDefault();
                        }
                        return request;
                }
                public static Request setRequest(Request r){
                        return request = r;
                }

        }
}

================= 아래의 예 =================

getRequest를 찾으십시오. getRequestImpl ... setInstance를 호출하여 호출하기 전에 구현을 변경할 수 있습니다.

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author Mo. Joseph
 * @date 16 mar 2012
 **/

public abstract class Core {


    // ---------------------------------------------------------------        
    private static Core singleton; 
    private static Core getInstance() {
        if ( singleton == null )
            setInstance( new Core.CoreDefaultImpl() );  // See bottom for CoreDefaultImpl

        return singleton;
    }    

    public static void setInstance(Core core) {
        Core.singleton = core;
    }
    // ---------------------------------------------------------------        



    // Static public method
    public static HttpServletRequest getRequest() {      
        return getInstance().getRequestImpl();
    }


    // A new implementation would override this one and call setInstance above with that implementation instance
    protected abstract HttpServletRequest getRequestImpl();




    // ============================ CLASSES =================================

    // ======================================================================
    // == Two example implementations, to alter getRequest() call behaviour 
    // == getInstance() have to be called in all static methods for this to work
    // == static method getRequest is altered through implementation of getRequestImpl
    // ======================================================================

    /** Static inner class CoreDefaultImpl */
    public static class CoreDefaultImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

     /** Static inner class CoreTestImpl : Alternative implementation */
    public static class CoreTestImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return new MockedRequest();
        }
    }       

}

다음과 같이 사용됩니다 :

static {
     Core.setSingleton(new Core.CoreDefaultImpl());

     // Or

     Core.setSingleton(new Core.CoreTestImpl());

     // Later in the application you might use

     Core.getRequest(); 

}

6
abstract static질문에 나와 있는 방법 의 예를 어디에서 제공했으며 JAVA 에서 굵은 글씨로 작성 했는지 이해하지 못했습니다 . 이것은 완전히 오해입니다.
Blip

1
자체적으로 추상 정적으로 정의 할 수는 없지만이 패턴 / 해킹을 사용하여 정적 메소드의 구현을 변경할 수있는 유사한 결과를 얻을 수 있습니다. 거의 오도되지 않습니다. 다른 의미를 사용하여 수행 할 수 있습니다.
mmm

다음은 추상 클래스를 확장 한 다음 정적 메서드를 자식에 넣는 예입니다. 이것은 어떤 식 으로든 JAVA에서 완료 될 수있는 예가 아닙니다.
스쿠버 스티브

2
@ScubaSteve 첫째, 당신은 결론에 틀렸다. 둘째, 동일한 결과를 얻습니다. 클래스에 대한 정적 액세스는 다른 구현에 의해 변경 될 수 있음을 의미합니다. 정적 키워드를 추상적으로 만들 수 있다고 말하는 것은 대답이 아니지만이 패턴을 사용하면 정적 메소드를 사용하고 여전히 구현을 변경할 수 있습니다. 그것은 글로벌화의 부정적인 영향을 미치지 만 테스트 / 제품 / 개발 환경에서는 우리를 위해 속임수를 사용합니다.
mmm

5
  • 추상 메소드는 서브 클래스에서 대체 될 수 있도록 정의됩니다. 그러나 정적 메서드는 재정의 할 수 없습니다. 따라서 추상 정적 메소드를 갖는 것은 컴파일 타임 오류입니다.

    이제 다음 질문은 정적 메소드를 재정의 할 수없는 이유입니다.

  • 정적 메소드는 인스턴스가 아닌 특정 클래스에 속하기 때문입니다. 정적 메서드를 재정의하려고하면 컴파일이나 런타임 오류가 발생하지 않지만 컴파일러는 슈퍼 클래스의 정적 메서드를 숨길 수 있습니다.


4

정의에 따라 정적 메소드는 알 필요가 없습니다 this. 따라서 가상 메소드가 될 수 없습니다 (를 통해 사용 가능한 동적 서브 클래스 정보에 따라 오버로드 됨 this). 대신 정적 메서드 오버로드는 컴파일 타임에 사용할 수있는 정보를 기반으로합니다. 즉, 수퍼 클래스의 정적 메서드를 참조하면 수퍼 클래스 메서드를 호출하지만 하위 클래스 메서드는 호출하지 않습니다.

이것에 따르면 추상 정적 메소드는 정의 된 바디로 참조를 대체 할 수 없기 때문에 매우 쓸모가 없습니다.


4

나는 신-질 리온 답변이 이미 있지만 실제 해결책은 보이지 않습니다. 물론 이것은 실제 문제이며 Java에서이 구문을 제외시킬 이유가 없습니다. 원래 질문에는 이것이 필요할 수있는 컨텍스트가 없기 때문에 컨텍스트와 솔루션을 모두 제공합니다.

동일한 클래스 무리에 정적 메소드가 있다고 가정하십시오. 이 메소드는 클래스 고유의 정적 메소드를 호출합니다.

class C1 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C1
    }
}
class C2 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C2
    }
}

doWork()의 방법 C1C2동일하다. 이 calsses이 많이있을 수 있습니다 : C3 C4만약 등 static abstract허용했다, 당신은 같은 것을 수행하여 중복 코드를 제거하는 것입니다 :

abstract class C {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }

    static abstract void doMoreWork(int k);
}

class C1 extends C {
    private static void doMoreWork(int k) {
        // code for class C1
    }
}

class C2 extends C {
    private static void doMoreWork(int k) {
        // code for class C2
    }
}

그러나 static abstract조합이 허용되지 않기 때문에 컴파일 되지 않습니다. 그러나 이것은 static classconstruct 로 우회 할 수 있으며 다음과 같이 허용됩니다.

abstract class C {
    void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    abstract void doMoreWork(int k);
}
class C1 {
    private static final C c = new  C(){  
        @Override void doMoreWork(int k) {
            System.out.println("code for C1");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}
class C2 {
    private static final C c = new C() {
        @Override void doMoreWork(int k) {
            System.out.println("code for C2");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}

이 솔루션을 사용하면 복제되는 유일한 코드는

    public static void doWork() {
        c.doWork();
    }

1
최종 솔루션에서 추상 클래스 C에는 정적 메소드가 없으므로 C1 및 C2가 확장 메소드를 확장하고 doMoreWork () 메소드를 대체하고 다른 클래스가 인스턴스를 작성하고 필요한 메소드를 호출하게하지 않는 이유는 무엇입니까? 기본적으로 익명 클래스를 사용하여 클래스 C를 확장 한 다음 정적 인스턴스를 사용하여 C1 및 C2에서 정적 메소드 내에서 액세스를 허용하지만 동일한 것은 아닙니다.
sactiw

귀하가 여기에 제공 한 내용을 이해하지 못합니다. 최종 솔루션에서 당신이 할 수있는 일은 전화 C1.doWork()또는 C2.doWork()당신 은 전화를 걸 수 없습니다 C.doWork(). 또한 제공하지 않은 예제에서 작동하지 않는 경우 허용 된 경우 클래스 C는 어떻게 구현을 찾 doMoreWork()습니까? 마지막으로 컨텍스트 코드를 나쁜 디자인이라고 부릅니다. 왜? 일반적인 코드에 대한 함수를 생성 한 다음 클래스에서 정적 함수를 구현하는 대신 고유 한 코드에 대해 별도의 함수를 작성했기 때문 C입니다. 이것은 쉽다 !!!
Blip

2

두 개의 클래스가있는 가정, Parent그리고 Child. Parent입니다 abstract. 선언은 다음과 같습니다.

abstract class Parent {
    abstract void run();
}

class Child extends Parent {
    void run() {}
}

즉,의 인스턴스 는 실행 Parent방법을 지정해야합니다 run().

그러나 이제는 Parent그렇지 않다고 가정하십시오 abstract.

class Parent {
    static void run() {}
}

이것은 Parent.run()정적 메소드를 실행 한다는 의미입니다 .

abstract메소드 의 정의 는 "선언되었지만 구현되지 않은 메소드"로, 자체적으로 아무것도 리턴하지 않음을 의미합니다.

static메소드 의 정의 는 "호출되는 인스턴스에 관계없이 동일한 매개 변수에 대해 동일한 값을 리턴하는 메소드"입니다.

abstract메소드의 반환 값은 인스턴스 변화에 따라 변경됩니다. static방법은하지 않습니다. static abstract방법은 거의 반환 값이 일정한 방법이지만 아무것도 반환하지 않습니다. 이것은 논리적 모순입니다.

또한 static abstract방법 에 대한 이유는별로 없습니다 .


2

정적 메소드는 기능에 정적으로 바인딩되는 동안 동적 바인딩을 달성하기 위해 추상화가 수행되므로 추상 클래스는 정적 메소드를 가질 수 없습니다. 정적 메소드는 인스턴스 변수에 종속되지 않는 동작을 의미하므로 인스턴스 / 객체가 필요하지 않습니다. 정적 메소드는 객체가 아닌 클래스에 속합니다. 그것들은 모든 객체와 공유되는 PERMGEN이라는 메모리 영역에 저장됩니다. 추상 클래스의 메소드는 해당 기능에 동적으로 바인딩됩니다.


1
추상 클래스는 반드시 정적 메소드를 가질 수 있습니다. 그러나 정적 추상 메소드는 아닙니다.
JacksOnF1re

2

같은 방법 선언 static우리는 클래스 이름으로 메소드를 호출 할 수 있습니다 그 클래스 인 경우 수단 abstract뿐만 아니라, 그것이 어떤 시체를 포함하지 않는로 전화 이해되지 않는다, 따라서 우리가 두 방법을 선언 할 수 없습니다 staticabstract.


2

추상 메소드는 클래스에 속하고 구현 클래스로 대체 할 수 없으므로 동일한 서명을 가진 정적 메소드가 있더라도 메소드를 숨기고 재정의하지 않습니다. 따라서 본문을 얻지 못하므로 추상 메소드를 정적으로 선언하는 것은 중요하지 않으므로 컴파일 시간 오류가 발생합니다.


1

클래스의 인스턴스없이 정적 메서드를 호출 할 수 있습니다. 예제에서는 foo.bar2 ()를 호출 할 수 있지만 foo.bar ()는 호출 할 수 없습니다. bar의 경우 인스턴스가 필요하기 때문입니다. 다음 코드가 작동합니다.

foo var = new ImplementsFoo();
var.bar();

정적 메소드를 호출하면 항상 동일한 코드로 실행됩니다. 위의 예제에서 ImplementsFoo에서 bar2를 재정의하더라도 var.bar2 ()를 호출하면 foo.bar2 ()가 실행됩니다.

bar2에 구현이없는 경우 (추상적 인 의미) 구현없이 메소드를 호출 할 수 있습니다. 매우 해 롭습니다.


1
또한 인스턴스없이 추상 정적 메서드를 호출 할 수 있지만 자식 클래스에서 구현을 만들어야합니다. 그것은 정확하게 다형성이 아니지만, 그것을 해결하는 유일한 방법은 구체적인 자식이 "정적 정적"메소드를 "필요한"인터페이스를 구현하도록하는 것입니다. 지저분하지만 실행 가능합니다.
fijiaaron

3
실제로, 나는 틀렸다. 인터페이스에는 정적 메서드를 사용할 수 없습니다. 언어 결함.
fijiaaron

1

인터페이스의 메소드 (부모 클래스의 추상 메소드처럼 작동하는)가 정적이 아닌 이유의 형태 로이 질문에 대한 답을 찾았습니다. 여기에 전체 답변이 있습니다 (내 것이 아님).

기본적으로 정적 메소드는 컴파일 타임에 바인딩 될 수 있습니다.이를 호출하려면 클래스를 지정해야하기 때문입니다. 이것은 메소드를 호출하는 참조 클래스가 컴파일 타임에 알 수없는 인스턴스 메소드와 다릅니다 (따라서 어떤 코드 블록이 호출되는지는 런타임에만 결정될 수 있음).

정적 메소드를 호출하는 경우 이미 구현 된 클래스 또는 직접 서브 클래스를 알고 있습니다. 정의하면

abstract class Foo {
    abstract static void bar();
}

class Foo2 {
    @Override
    static void bar() {}
}

그런 다음 모든 Foo.bar();전화는 분명히 불법이며 항상을 사용 Foo2.bar();합니다.

이를 염두에두고 정적 추상 메소드의 유일한 목적은 이러한 메소드를 구현하도록 서브 클래스를 강제하는 것입니다. 당신은 처음이 아주 잘못 생각할 수도 있지만, 제네릭 형식 매개 변수가있는 경우 <E extends MySuperClass>는 인터페이스를 통해 그 보장에 좋을 것이다 E.doSomething(). 유형 삭제 제네릭으로 인해 컴파일시에만 존재한다는 점을 명심하십시오.

그렇다면 유용할까요? 예, 아마도 Java 8이 인터페이스에서 정적 메소드를 허용하는 이유입니다 (기본 구현에서만 가능). 클래스에서 기본 구현으로 정적 메소드를 추상화하지 않는 이유는 무엇입니까? 기본 구현을 가진 추상 메소드가 실제로 구체적인 메소드이기 때문입니다.

기본 구현이없는 추상 / 인터페이스 정적 메소드가 아닌 이유는 무엇입니까? 분명히, Java가 실행해야 할 코드 블록을 식별하는 방식 때문입니다 (제 답변의 첫 부분).


1

abstract 클래스는 OOPS 개념이고 정적 멤버는 OOPS의 일부가 아닙니다 ...
이제 인터페이스에서 정적 완료 메소드를 선언 할 수 있으며 인터페이스 내에서 메인 메소드를 선언하여 인터페이스를 실행할 수 있습니다.

interface Demo 
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}

0

추상 정적 메소드를 사용한다는 아이디어는 해당 메소드에 대해 특정 추상 클래스를 직접 사용할 수는 없지만 첫 번째 파생만이 정적 메소드 (또는 제네릭 : 제네릭의 실제 클래스)를 구현할 수 있다는 것입니다. 사용하다).

이런 식으로 sortableObject 추상 클래스를 만들거나 정렬 옵션의 매개 변수를 정의하는 (자동) 추상 정적 메서드와의 인터페이스를 만들 수 있습니다.

public interface SortableObject {
    public [abstract] static String [] getSortableTypes();
    public String getSortableValueByType(String type);
}

이제 이러한 모든 객체에 대해 동일한 기본 유형별로 정렬 할 수있는 정렬 가능한 객체를 정의 할 수 있습니다.

public class MyDataObject implements SortableObject {
    final static String [] SORT_TYPES = {
        "Name","Date of Birth"
    }
    static long newDataIndex = 0L ;

    String fullName ;
    String sortableDate ;
    long dataIndex = -1L ;
    public MyDataObject(String name, int year, int month, int day) {
        if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
        if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
        this.fullName = name ;
        this.sortableDate = MyUtils.createSortableDate(year,month,day);
        this.dataIndex = MyDataObject.newDataIndex++ ;
    }
    public String toString() {
        return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
    }

    // override SortableObject 
    public static String [] getSortableTypes() { return SORT_TYPES ; }
    public String getSortableValueByType(String type) {
        int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
        switch(index) {
             case 0: return this.name ;
             case 1: return this.sortableDate ;
        }
        return toString(); // in the order they were created when compared
    }
}

이제는

public class SortableList<T extends SortableObject> 

유형을 검색하고, 팝업 유형을 선택하여 정렬 할 유형을 선택하고 해당 유형에서 데이터를 가져 와서 목록을 수정하고, 정렬 유형을 선택할 때 자동으로 할 수있는 추가 기능을 hainv 할 수 있습니다. 새 항목을 정렬합니다. SortableList의 인스턴스는 "T"의 정적 메소드에 직접 액세스 할 수 있습니다.

String [] MenuItems = T.getSortableTypes();

인스턴스를 사용해야 할 때의 문제점은 SortableList에 아직 항목이 없지만 선호하는 정렬을 제공해야한다는 것입니다.

Cheerio, Olaf.


0

먼저, 추상 클래스에 대한 요점-추상 클래스는 인스턴스화 할 수 없습니다 ( wiki 참조 ). 그래서, 당신은 만들 수 없습니다 어떤 추상 클래스의 인스턴스를.

이제 java가 정적 메소드를 처리하는 방법은 해당 클래스의 모든 인스턴스 와 메소드를 공유하는 것입니다 .

따라서 클래스를 인스턴스화 할 수 없으면 추상 메소드가 확장되기를 구걸하기 때문에 해당 클래스에 추상 정적 메소드를 가질 수 없습니다.

팔.


추상 클래스의 모든 메소드는 클래스를 확장하여 실행해야하므로 변명의 여지가 없습니다. 추상 클래스를 확장 할 수 있습니다 (동시에 추상 메소드 구현). 따라서 이것은 설명으로 작동하지 않습니다.
Pere

0

Java doc에 따라 :

정적 메소드는 객체가 아니라 정의 된 클래스와 연관된 메소드입니다. 클래스의 모든 인스턴스는 정적 메소드를 공유합니다

Java 8에서는 기본 메소드와 함께 인터페이스에서 정적 메소드도 허용됩니다. 이를 통해 라이브러리에서 헬퍼 메소드를 쉽게 구성 할 수 있습니다. 우리는 별도의 클래스가 아닌 동일한 인터페이스에서 인터페이스에 특정한 정적 메소드를 유지할 수 있습니다.

이에 대한 좋은 예는 다음과 같습니다.

list.sort(ordering);

대신에

Collections.sort(list, ordering);

정적 메소드를 사용하는 또 다른 예는 doc 자체 에도 있습니다.

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

0

'abstract'는 메서드가 재정의되고 'static'메서드를 재정의 할 수 없음을 의미하기 때문입니다.


이 답변은 이전 답변에서 아직 해결하지 않은 내용을 추가합니다.
MarsAtomic

@ MarsAtomic 가장 인기있는 답변보다 더 적절하다고 생각합니다. 또한 간결합니다.
Praveen Kumar

그런 다음 충분한 답변이있을 때 이전 답변을 편집하여 개선 할 수 있습니다. 중복 답변을 만들어 신호에 노이즈를 추가하기 만하면됩니다. 자신 만의 고유 한 규칙을 만들고 다른 사람이 따르기를 기대하지 말고 Stack Overflow의 정해진 규칙과 관습을 따르십시오.
MarsAtomic

동의하지 않습니다. 답변을 다른 답변, 특히 최고 투표 답변과 비교해주세요.
Praveen Kumar

"왜 안돼?" 답변 : "할 수 없기 때문에".
JacksOnF1re

0

일반 메소드는 서브 클래스로 대체되고 기능이 제공 될 때 추상적 일 수 있습니다. 클래스 FooBar1, Bar2, Bar3기타 등등 으로 확장 되었다고 상상해보십시오 . 따라서 각자 필요에 따라 추상 클래스의 자체 버전을 갖게됩니다.

이제 정의에 의한 정적 메소드는 클래스에 속하며 클래스의 오브젝트 또는 서브 클래스의 오브젝트와 관련이 없습니다. 그들은 존재하지 않아도 클래스를 인스턴스화하지 않고 사용할 수 있습니다. 따라서 준비가되어야하며 서브 클래스에 의존하여 기능을 추가 할 수 없습니다.


0

abstract는 Abstract 메소드에 적용되는 키워드이므로 본문을 지정하지 마십시오. 정적 키워드에 대해 이야기하면 클래스 영역에 속합니다.


귀하의 답변에 대해 조금 자세히 설명하십시오.
Blip

0

클래스에서 정적 멤버 또는 정적 변수를 사용하는 경우 클래스 로딩시로드됩니다.


3
왜 이것이 문제가 될까요?
eis

-1

Java 8의 인터페이스를 사용하여이 작업을 수행 할 수 있습니다.

다음은 공식 문서입니다.

https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html


2
어떻게? 해결책을 찾았지만 찾을 수 없었습니다.
thouliha

응? 당신은 할 수 없습니다. 모든 정적 인터페이스 메소드는 인터페이스 클래스를 사용하여 호출해야합니다.
mmm

8
이 답변은 잘못되었으며 Java를 처음 사용하는 사람들에게 잘못 안내됩니다. 이것은 다른 정적 인 방법으로는 구현되고 언급 될 수없는 추상 정적 방법의 예를 보여줍니다
Blip

-1

클래스가 추상 클래스를 확장하는 경우 추상 메소드를 대체해야하며 이는 필수입니다. 정적 메서드는 컴파일 타임에 클래스 메서드가 해결되는 반면 재정의 된 메서드는 런타임 및 동적 다형성에 따라 해결되는 인스턴스 메서드입니다.


이 난장판을 대문자로 바꾸고 문장 부호를 입력하십시오.
Lorne의 후작
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.