programing

Java에서 정적 메서드를 추상화할 수 없는 이유는 무엇입니까?

goodcopy 2022. 8. 2. 23:40
반응형

Java에서 정적 메서드를 추상화할 수 없는 이유는 무엇입니까?

문제는 Java에서 추상 정적 메서드를 정의할 수 없다는 것입니다.예를들면

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

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

서투른 언어 설계단순히 추상 메서드를 사용하기 위해 인스턴스를 생성하는 것보다 직접 정적 추상 메서드를 호출하는 것이 훨씬 효과적입니다.특히 enum을 확장할 수 없는 경우의 회피책으로 추상 클래스를 사용하는 경우에는 더욱 그렇습니다.이것은 또 다른 설계상의 불량 예입니다.다음 릴리즈에서는 이러한 제약이 해결되기를 바랍니다.

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

abstract메서드에 대한 주석은 메서드가 하위 클래스에서 재정의되어야 함을 나타냅니다.

a''입니다.staticmember 필드의해 수 (지향 ).member(SmallTalk ).member(SmallTalk는 SmallTalk를 참조해 주세요.SmallTalk 참 small ) 。 a.static멤버는 숨겨질 수 있지만 덮어쓰기와는 근본적으로 다릅니다.

는 서브클래스로 수 에, 「」는 「」를 참조해 .abstract주석을 적용할 수 없습니다.

참고로 다른 언어에서는 인스턴스 상속과 마찬가지로 정적 상속을 지원합니다.구문의 관점에서 이러한 언어에서는 일반적으로 클래스 이름을 문장에 포함해야 합니다.예를 들어 Java에서는 ClassA에서 코드를 쓰고 있다고 가정하면 다음과 같은 스테이트먼트가 됩니다(methodA()가 스태틱메서드이며 시그니처가 같은 인스턴스메서드가 존재하지 않는 경우).

ClassA.methodA();

그리고.

methodA();

SmallTalk에서는 클래스 이름은 옵션이 아니기 때문에 구문은 다음과 같습니다(SmallTalk에서는 를 사용하여 "서브젝트"와 "버전"을 구분하지 않고 대신 스테이트 엔드 터미네이터로 사용합니다).

ClassA methodA.

클래스 이름은 항상 필요하기 때문에 클래스 계층을 통과하여 메서드의 올바른 "버전"을 항상 확인할 수 있습니다.그게 중요한지 모르겠지만, 가끔씩은 내가 그리워하는 걸 놓치곤 해.static처음 시작할 때 Java에서 정적 상속이 부족하다는 사실에 물렸습니다.또한 SmallTalk(그리고 이처럼 program-by-contract을 지지하지 않습니다.)duck-typed 있다.또한 SmallTalk는 오리 타입이므로 계약별 프로그램은 지원하지 않습니다.따라서, 그것에는 그러므로,그것은 없닸다.abstractModifier 클래스 멤버.클래스 멤버의 수식자.

나는 또한 같은 질문을 했습니다, 여기 왜가 있습니다.

Abstract 클래스에서 말하는 바와 같이 구현은 제공되지 않으며 하위 클래스에서 제공하는 것은 허용됩니다.

따라서 서브클래스는 Superclass의 메서드를 덮어써야 합니다.

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

스태틱 멤버와 메서드는 컴파일 시간 요소이기 때문에 오버라이드(런타임 폴리몰피즘)가 아닌 스태틱메서드의 오버로드(컴파일 타임 폴리몰피즘)가 허용됩니다.

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

Java Universe에서는 허용되지 않는 추상 스태틱은 없습니다.

이것은 형편없는 언어 디자인이며, 그것이 가능하지 않은 이유에 대해서는 정말로 이유가 없습니다.

실제로 **Java**에서 를 모방하여 적어도 자신의 구현을 수정할 수 있도록 하는 패턴 또는 방법을 소개합니다.

public static abstract class Request {                 

        // Static method
        public static void doSomething() {
                get().doSomethingImpl();
        }
        
        // Abstract method
        abstract void doSomethingImpl();

        /////////////////////////////////////////////
        private static Request SINGLETON;
        private static Request get() {
            if ( SINGLETON == null ) {
                // If set(request) is never called prior,
                // it will use a default implementation. 
                return SINGLETON = new RequestImplementationDefault();
            }
            return SINGLETON;
        }
        public static Request set(Request instance){
            return SINGLETON = instance;
        }
        /////////////////////////////////////////////
}

두 가지 구현:

/////////////////////////////////////////////////////

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

/////////////////////////////////////////////////////

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

/////////////////////////////////////////////////////

다음과 같이 사용할 수 있습니다.

Request.set(new RequestImplementationDefault());

// Or

Request.set(new RequestImplementationTest());

// Later in the application you might use

Request.doSomething();

이를 통해 메서드를 정적으로 호출할 수 있지만 테스트 환경의 구현 예를 변경할 수 있습니다.

이론적으론, 당신은이론상으로는 이 작업을로 이것을 할 수 있다.ThreadLocal잘 다음 다른 스레드 컨텍스트에 대신보다는 완전히로 여기에서 보여진 세계적인 인스턴스를 설정할 수 있을, 그때 또한 할 여기서 보는 것처럼 완전히 글로벌하지 않고 스레드 컨텍스트별로 인스턴스를 설정할 수 있습니다 수 있을 것이다.Request.withRequest(anotherRequestImpl, () -> { ... })또는 유사한.또는 이와 유사합니다.

실제 세상의 보통 실제 세계에서는 일반적으로 다음과 같은 기능이 필요하지 않습니다를 요구하지 않는다.ThreadLocal일반적으로 테스트 환경의 구현을 글로벌하게 변경할 수 있습니다.

주의: 이 작업의 유일한 목적은 정적 방법이 제공하는 직접적이고 쉽게 그리고 클린리 방법을 호출할 수 있는 방법을 유지하는 동시에 약간 더 복잡한 구현의 비용으로 구현 방식을 전환할 수 있도록 하는 것입니다.

이것은 통상적으로 변경할 수 없는 정적 코드를 피하기 위한 패턴일 뿐입니다.

  • 추상 메서드는 하위 클래스에서 재정의될 수 있도록만 정의됩니다.단, 스태틱 메서드는 덮어쓸 수 없습니다.따라서 추상적인 정적 메서드를 갖는 것은 컴파일 시 오류입니다.

    다음 질문은 왜 스태틱 메서드를 덮어쓸 수 없는가 하는 것입니다.

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

정적 메서드 정의에 따르면정적 메서드는알 필요가 없습니다 정의상 알 필요가 없다.this. 따라서, 될 수 없는 가상 메서드(그 동적의 하위 분류별 정보따라서가상메서드일 수 없습니다를 통해 가능한(이 메서드는 를 통해 이용 가능한 동적 서브클래스 정보에 따르면 따라고 있는 오버로드됩니다).this);대신, 정적 메서드 오버로드는 컴파일 시간(이:일단 슈퍼 클래스의 정적 메서드를 말한다, 즉 슈퍼 클래스 메서드, 그러나 결콘 서브 클래스 메서드를 일컫는 말)에 증거에 기초한다.대신 스태틱 메서드의 오버로드는 컴파일 시에 이용 가능한 정보만을 기반으로 합니다(즉, 슈퍼클래스의 스태틱 메서드를 참조하면 슈퍼클래스 메서드를 호출하지만 서브클래스 메서드는 호출하지 않습니다).

이에 따르면 추상 정적 메서드는 정의된 바디에 의해 참조가 대체되지 않기 때문에 매우 유용하지 않습니다.

나는 이미 수많은 해답이 있다는 것을 알지만 실질적인 해결책은 보이지 않는다.물론 이것은 진짜 문제이며 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()의 메서드의 메서드C1 ★★★★★★★★★★★★★★★★★」C2이런.C3 C4 타타 etc etc etc etc 、 etc 、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 class constructure(constructure):

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();
    }

두 클래스가 가정해 .Parent ★★★★★★★★★★★★★★★★★」ChildParentabstract을 사용하다

abstract class Parent {
    abstract void run();
}

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

, 「」의 임의의 Parent에는 어떻게 명시해야 합니다.run()행됩니니다다

아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,Parentabstract.

class Parent {
    static void run() {}
}

, ,,Parent.run()는 스태틱 메서드를 실행합니다.

abstractmethod는 "선언되었지만 구현되지 않은 메서드"로, 그 자체로는 아무것도 반환하지 않습니다.

static입니다.method는 "같은 값을 반환하는 메서드입니다.

abstract메서드의 반환 값은 인스턴스가 변경됨에 따라 변경됩니다. a.static메서드는 그렇지 않습니다. a.static abstract되는 것은 입니다.이것은 논리적인 모순이다.

'아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,static abstract★★★★★★ 。

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

를 「」로 한다.static으로 그 할 수 , 그가 「」인 는, 「」라고 하는 입니다.abstract 본문이 되어 있지 않기 에, 「」, 「」, 「」, 「」, 「」, 「」, 「」의 양쪽 모두의 방법을 선언할 수 .static ★★★★★★★★★★★★★★★★★」abstract.

추상 메서드는 클래스에 속하므로 구현 클래스에서 재정의할 수 없습니다.같은 시그니처를 가진 스태틱 방식이 있는 경우에도 이 방식은 덮어쓰지 않습니다.따라서 추상적인 방법을 정적인 것으로 선언하는 것은 중요하지 않습니다. 그것은 결코 몸을 얻지 못할 것입니다.따라서 컴파일 시간 오류가 발생합니다.

클래스의 인스턴스 없이 스태틱메서드를 호출할 수 있습니다.이 예에서는 foo.bar2를 호출할 수 있지만 bar에는 인스턴스가 필요하기 때문에 foo.bar을 호출할 수 없습니다.다음 코드가 작동합니다.

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

정적 메서드를 호출하면 항상 동일한 코드가 실행됩니다.위의 예에서는 ImplementsFoo에서 bar2를 재정의해도 var.bar2()에 대한 콜은 foo.bar2()를 실행합니다.

bar2가 구현되지 않은 경우(추상적인 의미), 구현되지 않은 메서드를 호출할 수 있습니다.그것은 매우 해롭다.

왜 인터페이스의 메서드(부모 클래스의 추상 메서드처럼 동작)가 스태틱할 수 없는지에 대한 이 질문에 대한 답을 찾은 것 같습니다.이것이 (내 것이 아니라) 완전한 답이다

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

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

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가 실행하는 코드 블록을 식별하는 방법(내 답변의 첫 부분) 때문인 것 같습니다.

추상 클래스는 OOPS 개념이고 정적 멤버는 OOPS의 일부가 아니기 때문입니다.
여기서 한 것은 를 선언할 수 할 수 입니다.

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

왜냐하면 추상적인 메호드는 항상 서브클래스별로 구현이 필요하기 때문이다.단, 어떤 메서드를 static으로 하면 이 메서드는 덮어쓸 수 없습니다.

abstract class foo {
    abstract static void bar2(); 
}


class Bar extends foo {
    //in this if you override foo class static method then it will give error
}

추상 정적 메서드를 갖는다는 개념은 특정 추상 클래스를 그 메서드에 직접 사용할 수는 없지만 첫 번째 파생 모델만 해당 정적 메서드(또는 제네릭스의 경우 사용하는 일반 클래스의 실제 클래스)를 구현할 수 있다는 것입니다.

이렇게 하면 sortable Object 추상 클래스를 만들거나 정렬 옵션의 파라미터를 정의하는 (auto-abstract static 메서드와 인터페이스할 수 있습니다.

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> 

타입을 취득할 수 있는 팝업메뉴를 작성하여 정렬할 타입을 선택하고 해당 타입에서 데이터를 취득하여 목록을 리포트할 수 있는 것 외에 정렬 타입이 선택되었을 때 새로운 아이템을 자동으로 등록할 수 있는 추가기능이 있습니다.SortableList 인스턴스는 "T"의 정적 메서드에 직접 액세스할 수 있습니다.

String [] MenuItems = T.getSortableTypes();

인스턴스를 사용해야 하는 경우의 문제는 Sortable List에 아직 항목이 없을 수 있지만 이미 기본 정렬을 제공해야 한다는 것입니다.

치어리더, 올라프.

첫째, 추상 클래스에 대한 키포인트 - 추상 클래스는 인스턴스화할 수 없습니다(wiki 참조).따라서 추상 클래스의 인스턴스를 만들 수 없습니다.

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

따라서 클래스를 인스턴스화할 수 없는 경우 추상 메서드가 확장되어야 하므로 해당 클래스는 추상 정적 메서드를 가질 수 없습니다.

짜잔.

Java 문서에 따르면:

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

Java 8에서는 디폴트 메서드와 함께 스태틱메서드가 인터페이스에서도 허용됩니다.이를 통해 라이브러리에서 도우미 메서드를 쉽게 구성할 수 있습니다.인터페이스에 고유한 스태틱메서드를 다른 클래스가 아닌 같은 인터페이스로 유지할 수 있습니다.

예를 들어 다음과 같습니다.

list.sort(ordering);

대신

Collections.sort(list, ordering);

static 메서드를 사용하는 다른 예는 문서 자체에도 나와 있습니다.

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));
    }    
}

'추상'은 메서드가 재정의됨을 의미하므로 'static' 메서드는 재정의할 수 없습니다.

일반 메서드는 서브클래스에 의해 오버라이드되어 기능과 함께 제공되는 경우 추상화할 수 있습니다.Foo됩니다.Bar1, Bar2, Bar3그 때문에, 각자는 자신의 요구에 따라서 독자적인 추상 클래스를 갖게 됩니다.

정의상 스태틱메서드는 클래스에 속하며 클래스의 오브젝트나 서브클래스의 오브젝트와는 관계가 없습니다.존재조차 필요 없습니다.클래스를 인스턴스화하지 않고 사용할 수 있습니다.따라서 즉시 사용할 수 있어야 하며 서브클래스에 기능을 추가하는 데 의존할 수 없습니다.

Abstract는 Abstract 메서드에 적용되는 키워드이기 때문에 본문을 지정하지 않습니다.static 키워드를 말하면 클래스 영역에 속합니다.

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

static과 abstract를 동시에 사용할 수 있는 경우는 두 수식어가 모두 중첩된 클래스 앞에 배치되는 경우입니다.

정적 메서드 클래스의 인스턴스를 만들지 않고도 정적 메서드를 호출할 수 있습니다.정적 메서드는 클래스의 객체가 아닌 클래스에 속합니다.정적 메서드는 정적 데이터 멤버에 액세스할 수 있으며 멤버의 값을 변경할 수도 있습니다.Abstract 키워드는 추상화를 구현하기 위해 사용됩니다.정적 메서드는 자식 클래스에서 재정의하거나 구현할 수 없습니다.따라서 정적 방법을 추상적으로 만드는 것은 의미가 없습니다.

이것은 Java 8 의 인터페이스로 실행할 수 있습니다.

다음은 이에 대한 공식 문서입니다.

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

클래스가 추상 클래스를 확장하면 추상 메서드를 재정의해야 하므로 이는 필수입니다.또한 정적 메서드는 컴파일 시에 해결되는 클래스 메서드이지만 재정의된 메서드는 실행 시 해결되는 동적 다형성을 따르는 인스턴스 메서드입니다.

언급URL : https://stackoverflow.com/questions/370962/why-cant-static-methods-be-abstract-in-java

반응형