인터페이스의 생성자?


148

인터페이스에서 생성자를 정의 할 수 없다는 것을 알고 있습니다. 그러나 나는 그것이 매우 유용 할 수 있다고 생각하기 때문에 이유가 궁금합니다.

따라서 클래스의 일부 필드가이 인터페이스의 모든 구현에 대해 정의되어 있는지 확인할 수 있습니다.

예를 들어 다음 메시지 클래스를 고려하십시오.

public class MyMessage {

   public MyMessage(String receiver) {
      this.receiver = receiver;
   }

   private String receiver;

   public void send() {
      //some implementation for sending the mssage to the receiver
   }
}

메시지 인터페이스를 구현하는 더 많은 클래스를 가질 수 있도록이 클래스에 대한 인터페이스를 정의하면 생성자가 아닌 send 메소드 만 정의 할 수 있습니다. 그렇다면이 클래스의 모든 구현에 실제로 수신기 세트가 있는지 어떻게 확인할 수 있습니까? 같은 방법을 사용하면 setReceiver(String receiver)이 방법이 실제로 호출되는지 확신 할 수 없습니다. 생성자에서 확인할 수 있습니다.



2
"생성자에서 [이 클래스의 모든 구현에는 실제로 수신자 세트가 있음]을 보장 할 수 있습니다." 그러나 아닙니다. 그렇게 할 수 없었습니다. 이러한 생성자를 정의 할 수 있다면 매개 변수는 구현 자에게 강력한 힌트 일 뿐이지 만 원하는 경우 간단히 무시하도록 선택할 수 있습니다.
Julien Silland

3
@ mattb 음, 그건 다른 언어입니다.
yesennes

답변:


129

당신이 묘사 한 것들 중 일부를 취하는 것 :

"따라서 클래스의 일부 필드가이 인터페이스의 모든 구현에 대해 정의되어 있는지 확인할 수 있습니다."

"메시지 인터페이스를 구현하는 더 많은 클래스를 가질 수 있도록이 클래스에 대한 인터페이스를 정의하면 생성자가 아닌 send 메소드 만 정의 할 수 있습니다."

...이 요구 사항은 정확히 추상 클래스 에 대한 것입니다.


그러나 @Sebi가 설명하는 유스 케이스 (부모 생성자에서 오버로드 된 메소드 호출)는 내 대답에 설명 된 것처럼 나쁜 생각입니다.
rsp

44
Matt는 분명히 사실이지만 추상 클래스는 단일 상속 제한으로 인해 사람들이 계층을 지정하는 다른 방법을 보도록 유도합니다.
CPerkins

6
이것은 사실이며 Sebi의 즉각적인 문제를 해결할 수 있습니다. 그러나 Java에서 인터페이스를 사용하는 한 가지 이유는 다중 상속을 가질 수 없기 때문입니다. 다른 것을 상속해야하기 때문에 "사물"을 추상 클래스로 만들 수없는 경우에는 문제가 남아 있습니다. 내가 해결책을 가지고 있다고 주장하는 것은 아닙니다.
Jay

7
@CPerkins 이것은 사실이지만 추상적 클래스를 사용하면 Sebi의 사용 사례를 해결할 것이라고 제안하지는 않습니다. 무엇이든 메소드 Message를 정의 하는 인터페이스 를 선언하는 것이 가장 좋으며 send(), Sebi가 Message인터페이스 구현을위한 "기본"클래스를 제공하려는 AbstractMessage경우 역시 제공해야합니다 . 추상 클래스는 인터페이스를 대신해서는 안되며 결코 제안하지 않았습니다.
matt b

2
이해 했어, 매트 나는 당신과 말다툼을하지 않았으며, 그것이 op가 원하는 것을 완전히 대체하는 것이 아니라고 지적했다 .
CPerkins

76

인터페이스에서 생성자를 허용하면 여러 인터페이스를 동시에 구현할 수 있다는 문제가 발생합니다. 클래스가 다른 생성자를 정의하는 여러 인터페이스를 구현할 때 클래스는 여러 생성자를 구현해야합니다. 각 생성자는 하나의 인터페이스 만 만족하지만 다른 인터페이스는 만족하지 않습니다. 이러한 각 생성자를 호출하는 객체를 생성하는 것은 불가능합니다.

또는 코드에서 :

interface Named { Named(String name); }
interface HasList { HasList(List list); }

class A implements Named, HasList {

  /** implements Named constructor.
   * This constructor should not be used from outside, 
   * because List parameter is missing
   */
  public A(String name)  { 
    ...
  }

  /** implements HasList constructor.
   * This constructor should not be used from outside, 
   * because String parameter is missing
   */
  public A(List list) {
    ...
  }

  /** This is the constructor that we would actually 
   * need to satisfy both interfaces at the same time
   */ 
  public A(String name, List list) {
    this(name);
    // the next line is illegal; you can only call one other super constructor
    this(list); 
  }
}

1
언어는 같은 일을 가능하게하여 짓을 할 수 없습니다class A implements Named, HashList { A(){HashList(new list()); Named("name");} }
마코

1
허용되는 경우 "인터페이스의 생성자"에 대한 가장 유용한 의미는 " new Set<Fnord>()나로 사용할 수있는 것을 제공합니다"를 의미하는 것으로 해석 될 수 있습니다 Set<Fnord>. 저자가 다른 것을 필요로하지 않는 것에 대한 구현을 Set<T>의도 한 HashSet<T>경우 인터페이스 new Set<Fnord>()는와 동의어로 간주 될 수 있습니다 new HashSet<Fnord>(). 클래스가 여러 인터페이스를 구현하는 데는 아무런 문제가 없습니다. new InterfaceName()단순히 인터페이스로 지정된 클래스를 구성하기 때문 입니다 .
supercat

반론 : 당신의 A(String,List)생성자는 지정된 생성자가 될 수 있고, A(String)A(List)호출 차 사람이 될 수 있습니다. 귀하의 코드는 반례가 아니며 가난한 것입니다.
Ben Leggiero

구현에서 모든 생성자 를 호출 하는 이유는 무엇입니까 ?! ctor로 더 많은 인터페이스를 구현하고, 하나는 String으로, 다른 하나는 int로 더 많은 인터페이스를 구현하는 경우 두 개의 ctor가 필요하지만 둘 중 하나 또는 사용할 수 있습니다. 해당 사항이 없으면 클래스는 두 인터페이스를 모두 구현하지 않습니다. 그래서 무엇!? (인터페이스에 ctor가없는 다른 이유가 있습니다).
kai

@kai 아니요, 인스턴스를 생성 할 때 두 인터페이스 생성자를 모두 호출해야합니다. 다시 말해 : 내 예에서 인스턴스는 이름과 목록을 모두 가지고 있으므로 각 인스턴스는 이름과 목록을 모두 인스턴스화해야합니다.
다니엘 쿨만

13

인터페이스는 API의 계약자, 즉 API의 구현 자와 사용자가 동의하는 메소드 세트를 정의합니다. 인터페이스에는 인스턴스화 된 구현이 없으므로 생성자가 없습니다.

설명하는 사용 사례는 생성자가 자식 클래스에서 구현되는 추상 메서드의 메서드를 호출하는 추상 클래스와 유사합니다.

여기서 기본 문제는 기본 생성자가 실행되는 동안 자식 개체가 아직 구성되지 않았으므로 예측할 수없는 상태라는 것입니다.

요약 : mindprod 를 인용하기 위해 부모 생성자에서 오버로드 된 메소드를 호출 할 때 문제가 발생 합니까 ?

일반적으로 생성자에서 최종이 아닌 메소드를 호출하지 않아야합니다. 문제는 파생 클래스의 인스턴스 이니셜 라이저 / 변수 초기화 가 기본 클래스의 생성자 후에 수행된다는 것 입니다.


6

해결 getInstance()방법은 인터페이스에서 메소드를 정의 하여 구현자가 처리해야하는 매개 변수를 인식하는 것입니다. 추상 클래스만큼 견고하지는 않지만 인터페이스보다 유연성이 뛰어납니다.

그러나이 해결 방법을 사용하려면 getInstance()이 인터페이스의 모든 객체를 인스턴스화 하기 위해를 사용해야합니다 .

예 :

public interface Module {
    Module getInstance(Receiver receiver);
}

5

인터페이스에는 서브 클래스에서 객체를 생성하는 동안 초기화 할 필요가없는 정적 필드 만 있으며 인터페이스의 메소드는 서브 클래스에서 실제 구현을 제공해야하므로 인터페이스에는 생성자가 필요하지 않습니다.

두 번째 이유는 서브 클래스의 객체 생성 중에 부모 생성자를 호출합니다. 그러나 둘 이상의 인터페이스가 구현 된 경우 인터페이스 생성자를 호출하는 동안 어떤 인터페이스의 생성자가 먼저 호출하는지에 대한 충돌이 발생합니다.


3

인터페이스의 모든 구현에 특정 필드가 포함되도록하려면 해당 필드 의 getter를 인터페이스에 추가하면됩니다 .

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • 캡슐화가 깨지지 않습니다.
  • 인터페이스를 사용하는 모든 사람에게 Receiver객체가 어떤 식 으로든 클래스에 전달되어야 한다는 것을 알 수 있습니다 (생성자 또는 설정자에 의해)

2

인터페이스 메소드에서 참조되지 않은 종속성은 인터페이스가 강제하는 것이 아니라 구현 세부 사항으로 간주되어야합니다. 물론 예외가있을 수 있지만 일반적으로 인터페이스가 동작이 예상되는대로 정의해야합니다. 주어진 구현의 내부 상태는 인터페이스의 디자인 문제가되어서는 안됩니다.


1

이유 (설명에서 발췌)에 대해서는 이 질문 을 참조하십시오 .

실제로 이와 같은 작업을 수행해야하는 경우 인터페이스가 아닌 추상 기본 클래스를 원할 수 있습니다.


1

인터페이스가 메소드 본문을 정의 할 수 없기 때문입니다. 그러나 인터페이스가 기본적으로 정의한 모든 메소드에 대한 추상 수정 자와 동일한 클래스에서 생성자를 정의해야합니다. 이것이 인터페이스에서 생성자를 정의 할 수없는 이유입니다.


0

이 Technic을 사용하는 예가 있습니다. 이 특정 예제에서 코드는 MyCompletionListener추상 클래스로 마스크 된 인터페이스 인 모의 를 사용하여 Firebase를 호출 하고 생성자와의 인터페이스입니다.

private interface Listener {
    void onComplete(databaseError, databaseReference);
}

public abstract class MyCompletionListener implements Listener{
    String id;
    String name;
    public MyCompletionListener(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

private void removeUserPresenceOnCurrentItem() {
    mFirebase.removeValue(child("some_key"), new MyCompletionListener(UUID.randomUUID().toString(), "removeUserPresenceOnCurrentItem") {
        @Override
        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {

        }
    });
    }
}

@Override
public void removeValue(DatabaseReference ref, final MyCompletionListener var1) {
    CompletionListener cListener = new CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (var1 != null){
                        System.out.println("Im back and my id is: " var1.is + " and my name is: " var1.name);
                        var1.onComplete(databaseError, databaseReference);
                    }
                }
            };
    ref.removeValue(cListener);
}

private액세스 수정자를 어떻게 사용할 수 interface있습니까?
Rudra

0

일반적으로 생성자는 객체와 관련하여 특정 클래스의 비 정적 멤버를 초기화하기위한 것입니다.

선언 된 메소드 만 있고 정의 된 메소드는 없으므로 인터페이스에 대한 오브젝트 작성이 없습니다. 선언 된 메소드에 객체를 만들 수없는 이유는 객체 생성이 정적이 아닌 멤버에 대해 일부 메모리 (힙 메모리)를 할당하는 것입니다.

JVM은 완전히 개발되어 사용할 준비가 된 멤버를위한 메모리를 생성합니다. 이러한 멤버를 기반으로 JVM은 필요한 메모리 양을 계산하고 메모리를 생성합니다.

선언 된 메소드의 경우, JVM은 현재로서는 구현이 수행되지 않으므로 선언 된 메소드에 필요한 메모리 양을 계산할 수 없습니다. 따라서 인터페이스를위한 객체 생성이 불가능합니다.

결론:

객체 생성이 없으면 생성자를 통해 비 정적 멤버를 초기화 할 기회가 없으므로 인터페이스 내부에서 생성자를 사용할 수 없습니다 (인터페이스 내부에서 생성자를 사용하지 않기 때문에)

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