Java에서 기본 키워드의 목적은 무엇입니까?


94

Java의 인터페이스는 클래스와 비슷하지만 인터페이스의 본문에는 추상 메서드final필드 (상수) 포함될 수 있습니다 .

최근에 다음과 같은 질문을 보았습니다.

interface AnInterface {
    public default void myMethod() {
        System.out.println("D");
    }
}

인터페이스 정의에 따라 추상 메서드 만 허용됩니다. 위 코드를 컴파일 할 수있는 이유는 무엇입니까? default키워드 는 무엇입니까 ?

반면에 아래 코드를 작성하려고 할 때 modifier default not allowed here

default class MyClass{

}

대신에

class MyClass {

}

누구든지 default키워드 의 목적을 말해 줄 수 있습니까 ? 인터페이스 내에서만 허용됩니까? default(액세스 수정 자 없음) 과 어떻게 다른 가요?


4
인터페이스의 기본 메소드는 Java 8에 추가되었습니다. 액세스 수정자가 아니라 기본 구현입니다.
에 란

2
@Eran은 인터페이스 정의를 위반하는 기본 메소드 도입을 생각하지 않습니까? : s
Ravi

2
인터페이스 정의를 변경했습니다. 그 정의는 이제 구식입니다.
Louis Wasserman 2015

2
람다를 지원하기 위해 도입되었습니다. 필요한 이유에 대한 자세한 내용은 Project Lambda에 대한 짚맨 제안에 있습니다.
단거리

답변:


74

interface구현을 제공 할 수있는 Java 8의 새로운 기능입니다 . Java 8 JLS-13.5.6에 설명되어 있습니다. 읽는 인터페이스 메서드 선언 (일부)

default메서드를 추가 하거나 메서드를에서 abstract로 변경해도 default기존 바이너리와의 호환성이 깨지지는 않지만 IncompatibleClassChangeError기존 바이너리가 메서드를 호출하려고 시도하면이 ( 가) 발생할 수 있습니다 . 이 오류는 한정 유형 T이 두 인터페이스 I및 의 하위 유형 인 경우 발생합니다. J여기서 둘 다 I및 동일한 서명과 결과를 사용하여 메서드를 J선언하고 둘 다 선언 default하지 I않고 J다른 인터페이스의 하위 인터페이스 도 아닙니다 .

JDK 8의 새로운 기능에는 (일부)

기본 메서드를 사용하면 라이브러리 인터페이스에 새 기능을 추가 할 수 있으며 해당 인터페이스의 이전 버전 용으로 작성된 코드와 바이너리 호환성을 보장 할 수 있습니다.


16
이제 인터페이스와 추상 클래스가 거의 같습니다. :)
Ravi

16
@jWeaver 인터페이스는 여전히 생성자, 필드, 개인 메서드 또는 equals / hashCode / toString 구현을 가질 수 없습니다.
루이 Wasserman

10
@Louis Wasserman : Java 9에서는 private메소드 를 가질 수 있습니다 .
Holger

6
@Dan Pantry : private메소드는 실제로 인터페이스의 일부가 아니지만 default구현 또는 상수 초기화 프로그램 내에서 도우미 메소드 역할을 할 수 있습니다 . 인터페이스 내에서 람다 식을 사용할 때 합성 private메서드가 생성 되므로 Java 8에는 이미 존재합니다 . 따라서 Java 9를 사용하면 합성이 아닌 람다 사용
Holger

14
@jWeaver 인터페이스와 클래스의 차이는 상태동작으로 줄어 듭니다 . 인터페이스는 동작을 전달할 수 있지만 클래스 만 상태를 가질 수 있습니다. (예 : 같음 / 해시 코드와 같은 필드, 생성자, 메소드는 상태에 대한 있습니다.)
브라이언 게츠

26

기본 메서드는 주로 람다 식을 지원하기 위해 Java 8에 추가되었습니다. 디자이너는 (영리하게 생각하기에) 인터페이스의 익명 구현을 만들기 위해 람다 구문을 만들기로 결정했습니다. 그러나 주어진 람다는 단일 메소드 만 구현할 수 있으며, 이는 매우 심각한 제한이 될 단일 메소드가있는 인터페이스로 제한됩니다. 대신 더 복잡한 인터페이스를 사용할 수 있도록 기본 메서드가 추가되었습니다.

default람다로 인해 도입 된 주장에 대한 설득력이 필요한 경우 , 2009 년 Mark Reinhold가 작성한 Project Lambda 의 스트로 맨 제안 에는 람다를 지원하기 위해 추가해야 할 필수 기능으로 '확장 방법'이 언급되어 있습니다.

다음은 개념을 보여주는 예입니다.

interface Operator {
    int operate(int n);
    default int inverse(int n) {
        return -operate(n);
    }
}

public int applyInverse(int n, Operator operator) {
    return operator.inverse(n);
}

applyInverse(3, n -> n * n + 7);

나는 깨달았지만 default람다를 지원 하는 방법을 설명해야 합니다. inverse기본값 이기 때문에 필요한 경우 구현 클래스로 쉽게 재정의 할 수 있습니다.


8
이것은 정말 정확하지 않습니다. Lambdas는 근사한 원인 일 수 있지만 실제로는 낙타의 등을 부러 뜨린 빨대였습니다. 진정한 동기는 인터페이스 진화를 가능하게하는 것이 었습니다 (기존 인터페이스가 새로운 동작을 지원하도록 호환성있게 진화 할 수 있도록 허용). 람다가 이러한 요구를 전면에 내세운 요인 일 수 있지만 기능은 그보다 더 일반적입니다.
Brian Goetz 2015

@BrianGoetz : IMHO, Java와 .NET 모두 기본 메서드가 처음부터 존재 했더라면 엄청난 혜택을 받았을 것입니다. 인터페이스 멤버 만 사용하는 인터페이스 구현에 대해 몇 가지 공통 작업을 수행 할 수 있지만 일부 구현에 더 효율적인 방법이있을 수있는 경우 인터페이스는 해당 작업에 대한 메서드를 정의하고 기본 구현을 제공해야합니다. 기본 구현을 지정할 수 없기 때문에 인터페이스에서 이러한 메서드를 생략해야하고 나중에 추가 할 수 없게되었습니다.
supercat 2015-07-23

@BrianGoetz 나는 기본 방법이 람다 이상의 중요한 가치를 가지고 있다는 데 동의합니다. 그러나 나는 그들을 포함하기로 결정하는 더 넓은 가치에 대해 당신이 줄 수있는 어떤 참조에 관심이 있습니다. 내 독서는 람다가 주된 이유 였다는 것입니다 (내 대답에서 '주로'라는 단어를 사용한 이유입니다).
단거리 선수

2
아마도이 문서가 도움이 될 것입니다 : cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html . 섹션 10은 "기본 메서드 (이전에는 가상 확장 메서드 또는 방어자 메서드라고 함)의 목적은 인터페이스가 처음 게시 된 후 호환 가능한 방식으로 발전 할 수 있도록하는 것입니다." 그런 다음 Lambda 친화적 인 방법이 인터페이스 진화 의 예시 로 인용됩니다 .
Brian Goetz 2015

2
@Kartik 당신은 잘못된 질문을하고 있습니다! 우리는 "컴파일러가 프로그램을 올바르게 구문 분석하는 데 필요한 절대 최소값"을 기준으로 구문을 선택하지 않습니다. 우리는 "무엇이 프로그래머의 의도를 독자들에게보다 즉각적으로 분명하게 만드는가"를 기준으로 선택합니다. 우리는 컴파일러 (. 그것은 사용자에 올 때, 우리는 이차적 쓰기, 읽기 먼저 디자인)에 대한 이차적 사용자를위한 최초의 설계 및
브라이언 게츠

16

Java 8에는 기본 메소드라는 새로운 개념이 도입되었습니다. 기본 메서드는 기본 구현이 있으며 기존 코드를 손상시키지 않고 인터페이스를 발전시키는 데 도움이되는 메서드입니다. 예를 살펴 보겠습니다.

 public interface SimpleInterface {
    public void doSomeWork();

    //A default method in the interface created using "default" keyword

    default public void doSomeOtherWork(){

    System.out.println("DoSomeOtherWork implementation in the interface");
       }
    }

 class SimpleInterfaceImpl implements SimpleInterface{

  @Override
  public void doSomeWork() {
  System.out.println("Do Some Work implementation in the class");
   }

 /*
  * Not required to override to provide an implementation
  * for doSomeOtherWork.
  */

 public static void main(String[] args) {
   SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
   simpObj.doSomeWork();
   simpObj.doSomeOtherWork();
      }
   }

출력은 다음과 같습니다.


인터페이스의 DoSomeOtherWork 구현 클래스에서 Do Some Work 구현


16

다른 답변에서 간과되었던 것은 주석에서의 역할이었습니다. Java 1.5에서 default키워드는 주석 필드에 기본값제공하는 수단으로 사용 되었습니다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Processor {
    String value() default "AMD";
}

인터페이스에서 기본 메소드를 정의 할 수 있도록 Java 8의 도입으로 사용량이 오버로드 되었습니다.

간과 된 또 다른 것 : 선언 default class MyClass {}이 유효하지 않은 이유 는 클래스가 전혀 선언 되는 방식 때문 입니다. 해당 키워드가 여기에 표시되도록 허용하는 언어 조항이 없습니다. 그러나 인터페이스 메서드 선언 에는 나타납니다 .


3

새로운 Java 8 기능 ( Default Methods )을 사용하면 인터페이스가 default키워드로 레이블이 지정되었을 때 구현을 제공 할 수 있습니다 .

예를 들면 :

interface Test {
    default double getAvg(int avg) {
        return avg;
    }
}
class Tester implements Test{
 //compiles just fine
}

Interface Test는 인터페이스를 사용하는 클래스에서 이러한 메서드를 구현할 필요없이 인터페이스가 메서드의 기본 구현을 제공 할 수 있도록하는 default 키워드를 사용합니다.

이전 버전과의 호환성 : 인터페이스가 수백 개의 클래스로 구현되어 있다고 상상해보십시오. 인터페이스를 수정하면 인터페이스를 구현하는 다른 많은 클래스에 필수적인 것은 아니지만 모든 사용자가 새로 추가 된 메서드를 구현해야합니다.

사실 및 제한 사항 :

1- 클래스 또는 추상 클래스가 아닌 인터페이스 내에서만 선언 될 수 있습니다.

2- 몸을 제공해야합니다

3- 인터페이스에서 사용되는 다른 일반적인 방법과 같이 public 또는 abstract로 간주되지 않습니다.


3

인터페이스의 기본 메소드를 사용하면 이전 코드를 손상시키지 않고 새로운 기능을 추가 할 수 있습니다.

Java 8 이전에는 새 메소드가 인터페이스에 추가되면 해당 인터페이스의 모든 구현 클래스가 새 기능을 사용하지 않더라도 새 메소드를 대체하도록 바인딩되었습니다.

Java 8에서는 default메소드 구현 전에 키워드를 사용하여 새 메소드에 대한 기본 구현을 추가 할 수 있습니다 .

익명의 클래스 또는 기능적 인터페이스를 사용하더라도 일부 코드가 재사용 가능하고 코드의 모든 곳에 동일한 로직을 정의하고 싶지 않다면 기본 구현을 작성하고 재사용 할 수 있습니다.

public interface YourInterface {
    public void doSomeWork();

    //A default method in the interface created using "default" keyword
    default public void doSomeOtherWork(){

    System.out.println("DoSomeOtherWork implementation in the interface");
       }
    }

    class SimpleInterfaceImpl implements YourInterface{

     /*
     * Not required to override to provide an implementation
     * for doSomeOtherWork.
     */
      @Override
      public void doSomeWork() {
  System.out.println("Do Some Work implementation in the class");
   }

 /*
  * Main method
  */
 public static void main(String[] args) {
   SimpleInterfaceImpl simpObj = new SimpleInterfaceImpl();
   simpObj.doSomeWork();
   simpObj.doSomeOtherWork();
      }
   }

2

The Java ™ Tutorials 에서 아주 좋은 설명을 찾을 수 있습니다 . 설명의 일부는 다음과 같습니다.

자동차를 작동하기 위해 호출 할 수있는 방법을 설명하는 산업 표준 인터페이스를 게시하는 컴퓨터 제어 자동차 제조업체와 관련된 예를 고려하십시오. 컴퓨터로 제어되는 자동차 제조업체가 비행과 같은 새로운 기능을 자동차에 추가하면 어떻게 될까요? 이러한 제조업체는 다른 회사 (예 : 전자 유도 기기 제조업체)가 소프트웨어를 비행 자동차에 적용 할 수 있도록 새로운 방법을 지정해야합니다. 이러한 자동차 제조업체는 이러한 새로운 비행 관련 방법을 어디에서 선언할까요? 원래 인터페이스에 추가하면 해당 인터페이스를 구현 한 프로그래머는 구현을 다시 작성해야합니다. 그것들을 정적 메소드로 추가하면 프로그래머는 그것들을 필수 핵심 메소드가 아닌 유틸리티 메소드로 간주 할 것입니다.

기본 메서드를 사용하면 라이브러리의 인터페이스에 새 기능을 추가하고 해당 인터페이스의 이전 버전 용으로 작성된 코드와 바이너리 호환성을 보장 할 수 있습니다.


1

기본 메서드를 사용하면 앱의 인터페이스에 새로운 기능을 추가 할 수 있습니다. 다중 상속 을하는데도 사용할 수 있습니다 . 기본 메서드 외에도 인터페이스에서 정적 메서드를 정의 할 수 있습니다. 이렇게하면 도우미 메서드를 쉽게 구성 할 수 있습니다.

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