[java] Java 인터페이스에서 정적 메소드를 정의 할 수없는 이유는 무엇입니까?

편집 : Java 8부터는 인터페이스에서 정적 메소드가 허용됩니다.

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

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

물론 이것은 작동하지 않습니다. 그런데 왜 안 되 겠어요?

가능한 문제 중 하나는 전화를 걸 때 발생하는 일입니다.

IXMLizable.newInstanceFromXML(e);

이 경우 빈 메소드 (예 : {})를 호출해야한다고 생각합니다. 모든 서브 클래스는 정적 메소드를 구현해야하므로 정적 메소드를 호출 할 때 모두 괜찮습니다. 왜 이것이 불가능합니까?

편집 : 나는 “자바의 방식이기 때문에”보다 더 깊은 답변을 찾고 있다고 생각합니다.

정적 메서드를 덮어 쓸 수없는 특별한 기술적 이유가 있습니까? 즉, Java 설계자가 왜 인스턴스 메소드를 대체 가능하지만 정적 메소드는 사용하지 않기로 결정 했습니까?

편집 : 내 디자인의 문제는 코딩 규칙을 적용하기 위해 인터페이스를 사용하려고한다는 것입니다.

즉, 인터페이스의 목표는 두 가지입니다.

  1. IXMLizable 인터페이스를 사용하여 클래스를 구현하는 클래스를 XML 요소로 변환 할 수 있기를 원합니다 (다형성을 사용하여 잘 작동 함).

  2. 누군가가 IXMLizable 인터페이스를 구현하는 클래스의 새 인스턴스를 만들고 싶다면 항상 newInstanceFromXML (Element e) 정적 생성자가 있음을 알게 될 것입니다.

인터페이스에 주석을 넣는 것 외에 다른 방법이 있습니까?



답변

Java 8은 정적 인터페이스 메소드를 허용합니다

Java 8을 사용하면 인터페이스 정적 메소드가있을 있습니다. 또한 구체적인 인스턴스 메소드를 가질 수 있지만 인스턴스 필드는 가질 수 없습니다.

여기에는 실제로 두 가지 질문이 있습니다.

  1. 왜 나쁜 옛날에는 인터페이스에 정적 메소드를 포함 할 수 없었습니까?
  2. 정적 메서드를 재정의 할 수없는 이유는 무엇입니까?

인터페이스의 정적 메소드

인터페이스가 이전 버전에서 정적 메소드를 가질 수 없었던 강력한 기술적 이유는 없었습니다. 이것은 중복 질문 의 포스터의해 훌륭하게 요약됩니다 . 정적 인터페이스 메소드는 처음으로 간주되었다 작은 언어 변화 하고 있었다 공식 제안 자바 7에 추가 할 수는 있지만, 나중에 된 예기치 못한 합병증으로 인해 하락했다.

마지막으로 Java 8에는 정적 인터페이스 메소드와 기본 구현을 통한 대체 가능한 인스턴스 메소드가 도입되었습니다. 그래도 인스턴스 필드를 가질 수 없습니다. 이러한 기능은 람다 식 지원의 일부이며 JSR 335의 H 부분 에서 자세한 내용을 읽을 수 있습니다 .

정적 메서드 재정의

두 번째 질문에 대한 대답은 조금 더 복잡합니다.

정적 메소드는 컴파일 타임에 해석 할 수 있습니다. 동적 디스패치는 컴파일러가 객체의 구체적인 유형을 결정할 수 없으므로 호출 할 메서드를 확인할 수없는 인스턴스 메서드에 적합합니다. 그러나 정적 메소드를 호출하려면 클래스가 필요하며 해당 클래스는 정적으로 알려져 있기 때문에 컴파일 타임에 동적 디스패치가 필요하지 않습니다.

여기에서 무슨 일이 일어나고 있는지 이해하려면 인스턴스 메소드 작동 방식에 대한 약간의 배경이 필요합니다. 실제 구현이 상당히 다르다고 확신하지만, 모델이 동작을 정확하게 관찰 한 메소드 디스패치 개념을 설명하겠습니다.

각 클래스에는 메소드를 구현하기 위해 메소드 서명 (이름 및 매개 변수 유형)을 실제 코드에 맵핑하는 해시 테이블이 있다고 가정하십시오. 가상 머신은 인스턴스에서 메소드를 호출하려고 시도 할 때 클래스에 대한 오브젝트를 조회하고 클래스 테이블에서 요청 된 서명을 찾습니다. 메소드 본문이 발견되면 호출됩니다. 그렇지 않으면 클래스의 부모 클래스가 얻어지고 조회가 반복됩니다. 이것은 메소드가 발견되거나 더 이상 상위 클래스가 없을 때까지 진행 NoSuchMethodError됩니다.

수퍼 클래스와 서브 클래스 모두 테이블에 동일한 메소드 서명에 대한 항목이있는 경우 서브 클래스 버전이 먼저 발생하고 수퍼 클래스 버전이 사용되지 않습니다. 이는 “재정의”입니다.

이제 객체 인스턴스를 건너 뛰고 서브 클래스로 시작한다고 가정 해 봅시다. 위와 같이 해결이 진행되어 일종의 “재정의 가능한”정적 메서드가 제공됩니다. 그러나 컴파일러가 런타임에 클래스에 대해 지정되지 않은 유형의 객체를 쿼리하기를 기다리는 대신 알려진 클래스에서 시작하기 때문에 모든 컴파일 타임에 해결이 가능합니다. 정적 메소드를 “재정의”하는 것은 항상 원하는 버전을 포함하는 클래스를 지정할 수 있기 때문에 의미가 없습니다.


생성자 “인터페이스”

다음은 질문에 대한 최근 수정 사항을 다루기위한 자료입니다.

의 각 구현에 대해 생성자와 같은 메소드를 효과적으로 위임하려는 것처럼 들립니다 IXMLizable. 1 분 동안 인터페이스로이를 시행하는 것을 잊고이 요구 사항을 충족하는 클래스가 있다고 가정하십시오. 어떻게 사용 하시겠습니까?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Foo새 객체를 “구성”할 때 구체적 유형의 이름을 명시 적으로 지정해야하기 때문에 컴파일러는 실제로 필요한 팩토리 메소드가 있는지 확인할 수 있습니다. 그렇지 않은 경우 어떻게해야합니까? 내가 구현할 수 있다면 IXMLizable은 “생성자”부족 것을, 나는 인스턴스를 생성하고 코드에 전달, 그것은 이다IXMLizable 필요한 모든 인터페이스를.

구성은 인터페이스가 아닌 구현의 일부입니다 . 인터페이스에서 성공적으로 작동하는 코드는 생성자를 신경 쓰지 않습니다. 어쨌든 생성자를 신경 쓰는 모든 코드는 구체적 유형을 알아야하며 인터페이스는 무시할 수 있습니다.


답변

이것은 이미 여기에 묻고 대답 했습니다.

내 답변을 복제하려면 :

인터페이스에서 정적 메소드를 선언 할 필요는 없습니다. 일반적인 호출 MyInterface.staticMethod ()로는 실행할 수 없습니다. 구현 클래스 MyImplementor.staticMethod ()를 지정하여 호출하면 실제 클래스를 알아야하므로 인터페이스에 포함되어 있는지 여부와 관련이 없습니다.

더 중요한 것은 정적 메소드가 재정의되지 않으며 수행하려는 경우 :

MyInterface var = new MyImplementingClass();
var.staticMethod();

static 규칙은 선언 된 var 유형에 정의 된 메소드를 실행해야한다고 말합니다. 이것은 인터페이스이므로 불가능합니다.

“result = MyInterface.staticMethod ()”를 실행할 수없는 이유는 MyInterface에 정의 된 메소드의 버전을 실행해야하기 때문입니다. 그러나 인터페이스이기 때문에 MyInterface에 정의 된 버전이있을 수 없습니다. 정의에 따른 코드는 없습니다.

“Java가 그렇게하기 때문에”이 정도라고 말할 수 있지만 실제로 결정은 다른 디자인 결정의 논리적 결과이기도합니다.


답변

일반적으로 이것은 팩토리 패턴을 사용하여 수행됩니다.

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}


답변

Java 8 의 출현으로 인터페이스에서 기본정적 메소드 를 작성할 수 있습니다.
docs.oracle/staticMethod

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

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

결과 : 6

팁 : 정적 인터페이스 메서드 호출은 클래스에 의해 구현 될 필요가 없습니다. 수퍼 클래스의 정적 메소드에 대해 동일한 규칙이 인터페이스의 정적 메소드에 적용되기 때문입니다.


답변

정적 메소드는 서브 클래스에서 재정의 될 수 없으므로 추상적 일 수 없습니다. 인터페이스의 모든 메소드는 사실상 추상적입니다.


답변

Java 인터페이스에서 정적 메소드를 정의 할 수없는 이유는 무엇입니까?

실제로는 Java 8에서 가능합니다.

Java doc에 따라 :

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

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

기본 방법의 예 :

list.sort(ordering);

대신에

Collections.sort(list, ordering);

정적 메소드의 예 ( 문서 자체에서) :

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


답변

인터페이스는 본질적으로 클래스가 아닌 객체 인스턴스에 연결된 다형성과 관련이 있습니다. 따라서 정적은 인터페이스의 맥락에서 의미가 없습니다.