기본 방법은 무엇입니까?
Java 8 릴리스 이후에는 인터페이스에 새 메소드를 추가할 수 있지만 인터페이스는 여전히 구현 클래스와 호환됩니다. 개발한 라이브러리는 여러 개발자가 널리 사용할 수 있으므로 이는 중요합니다. Java 8 이전에는 인터페이스가 클래스 라이브러리에 게시된 후 새 메서드가 인터페이스에 추가되면 이 인터페이스를 구현한 애플리케이션이 새 버전의 인터페이스를 사용하는 동안 충돌이 발생할 위험이 있었습니다.
Java 8에는 그런 위험이 없나요? 대답은 '아니요'입니다.
인터페이스에 기본 메서드를 추가하면 일부 구현 클래스를 사용하지 못할 수 있습니다.
먼저 기본 메소드의 세부사항을 살펴보겠습니다.
Java 8에서는 인터페이스의 메서드를 구현할 수 있습니다(Java 8의 정적 메서드도 인터페이스에서 구현할 수 있지만 이는 또 다른 주제입니다). 인터페이스에 구현된 메서드를 기본 메서드라고 하며 키워드 default를 한정자로 식별합니다. 클래스가 인터페이스를 구현할 때 인터페이스에 이미 구현된 메서드를 구현할 수 있지만 필수는 아닙니다. 이 클래스는 기본 메서드를 상속합니다. 이것이 인터페이스가 변경될 때 구현 클래스를 변경할 필요가 없는 이유입니다.
다중 상속은 어떻습니까?
클래스가 둘 이상의 인터페이스(예: 두 개)를 구현하고 이러한 인터페이스가 동일한 기본 메서드를 갖는 경우 상황이 매우 복잡해집니다. 클래스는 어떤 기본 메소드를 상속합니까? 어느 것도 아니다! 이 경우 클래스 자체(직접 또는 상속 트리의 상위 클래스)가 기본 메서드를 구현해야 합니다.
한 인터페이스가 기본 메서드를 구현하고 다른 인터페이스가 기본 메서드를 추상으로 선언하는 경우에도 마찬가지입니다. Java 8은 모호성을 피하고 엄격함을 유지하려고 노력합니다. 메서드가 여러 인터페이스에 선언되면 기본 구현이 상속되지 않으며 컴파일 시간 오류가 발생합니다.
그러나 클래스를 컴파일한 경우에는 컴파일 시간 오류가 발생하지 않습니다. 이 시점에서 Java 8은 일관성이 없습니다. 나름의 이유가 있고, 여기에서는 자세히 설명하거나 깊이 논의하고 싶지 않습니다. (왜냐하면 버전이 출시되었고 토론 시간이 너무 길며 이 플랫폼에서는 그런 토론).
1. 두 개의 인터페이스와 하나의 구현 클래스가 있다고 가정합니다.
2. 인터페이스 중 하나가 기본 메서드 m()을 구현합니다.
3. 인터페이스와 구현 클래스를 함께 컴파일합니다.
4. m() 메서드를 포함하지 않는 인터페이스를 수정하고 m() 메서드를 abstract로 선언합니다.
5. 수정된 인터페이스를 별도로 다시 컴파일합니다.
6. 구현 클래스를 실행합니다.
1. 추상 메서드 m()이 포함된 인터페이스를 수정하고 기본 구현을 만듭니다.
2. 수정된 인터페이스 컴파일
3. 클래스 실행: 실패했습니다.
두 인터페이스가 동일한 메서드에 대한 기본 구현을 제공하는 경우 구현 클래스가 기본 메서드도 구현하지 않는 한(직접 또는 상속 트리의 상위 수준 클래스를 통해) 이 메서드를 호출할 수 없습니다.
예제 코드:
위의 예를 보여주기 위해 C.java에 대한 테스트 디렉터리를 만들었고 그 아래에 I1.java 및 I2.java를 저장하기 위한 3개의 하위 디렉터리가 있습니다. 테스트 디렉토리에는 C 클래스의 소스 코드 C.java가 포함되어 있습니다. 기본 디렉터리에는 컴파일하고 실행할 수 있는 인터페이스 버전이 포함되어 있습니다. I1에는 기본 구현이 포함된 m() 메서드가 포함되어 있고 I2에는 메서드가 포함되어 있지 않습니다.
구현 클래스에는 테스트에서 실행할 수 있는 기본 메서드가 포함되어 있습니다. m()을 호출하고 m()을 호출하지 않는 테스트를 쉽게 수행할 수 있도록 명령줄 매개변수가 있는지 확인합니다.
다음과 같이 코드 코드를 복사합니다.
~/github/test$ 고양이 C.java
공개 클래스 C는 I1, I2를 구현합니다.
공개 정적 무효 메인(String[] args) {
Cc = 새로운 C();
if(args.length == 0){
cm();
}
}
}
~/github/test$ 고양이 베이스/I1.java
공개 인터페이스 I1 {
기본 무효 m(){
System.out.println("안녕하세요 인터페이스 1");
}
}
~/github/test$ 고양이 베이스/I2.java
공개 인터페이스 I2 {
}
다음 명령줄을 사용하여 컴파일하고 실행합니다.
다음과 같이 코드를 복사합니다:~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
안녕하세요 인터페이스 1
Compatible 디렉터리에는 추상 메서드 m()이 있는 I2 인터페이스와 수정되지 않은 I1 인터페이스가 포함되어 있습니다.
다음과 같이 코드를 복사하세요:~/github/test$ cat Compatible/I2.java
공개 인터페이스 I2 {
무효 m();
}
이는 클래스 C를 컴파일하는 데 사용할 수 없습니다.
다음과 같이 코드를 복사합니다:~/github/test$ javac -cp .:호환 C.java
C.java:1: 오류: C는 추상이 아니며 I2의 추상 메서드 m()을 재정의하지 않습니다.
공개 클래스 C는 I1, I2를 구현합니다.
^
오류 1개
오류 메시지는 매우 정확합니다. 이전 컴파일에서 얻은 C.class가 있으므로 호환 가능한 디렉터리에서 인터페이스를 컴파일하면 구현 클래스를 실행할 수 있는 두 개의 인터페이스를 얻을 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
~/github/test$ javac Compatible/I*.java
~/github/test$ java -cp .:호환 C
안녕하세요 인터페이스 1
wrong이라는 세 번째 디렉토리에는 m() 메서드도 정의하는 I2 인터페이스가 포함되어 있습니다.
다음과 같이 코드 코드를 복사합니다.
~/github/test$ 고양이가 잘못되었습니다/I2.java
공개 인터페이스 I2 {
기본 무효 m(){
System.out.println("hello 인터페이스 2");
}
}
우리는 그것을 컴파일하는 데 수고를 들여야 합니다. m() 메서드가 두 번 정의되었더라도 정의된 메서드를 여러 번 호출하지 않는 한 구현 클래스는 계속 실행될 수 있습니다. 그러나 m() 메서드를 호출하는 한 즉시 실패합니다. 우리가 사용하는 명령줄 매개변수는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
~/github/test$ javac 틀림/*.java
~/github/test$ java -cp .:잘못된 C
스레드 "main" java.lang.In CompatibleClassChangeError의 예외: 충돌 중
기본 방법: I1.m I2.m
cm(C.java)에서
C.main에서(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/테스트$
결론적으로
인터페이스에 기본 구현을 추가하는 클래스 라이브러리를 Java 8 환경으로 이식하는 경우 일반적으로 문제가 없습니다. 적어도 Java8 클래스 라이브러리 개발자는 컬렉션 클래스에 기본 메서드를 추가할 때 그렇게 생각했습니다. 라이브러리를 사용하는 애플리케이션은 여전히 기본 메소드가 없는 Java 7 라이브러리에 의존합니다. 여러 개의 서로 다른 클래스 라이브러리를 사용하고 수정할 때 충돌이 발생할 가능성이 적습니다. 이것을 어떻게 피할 수 있습니까?
이전과 같이 클래스 라이브러리를 디자인합니다. 기본 방법을 사용할 때 이를 가볍게 여기지 마십시오. 최후의 수단으로 사용하지 마십시오. 다른 인터페이스와의 충돌을 피하려면 메소드 이름을 현명하게 선택하십시오. Java 프로그래밍 개발을 위해 이 기능을 사용하는 방법을 알아봅니다.