History of Java interface feature changes
원문 : https://www.vojtechruzicka.com/java-interface-history/
지나가며 글을 읽다가 빠른 번역(잘못된 번역이 많을 수 있습니다)을 해보았습니다.
Original interfaces
java 1.0의 interface는 두개의 type만 포함한다. constants와 public abstract methods.
Constant fields
Inteface는 field를 포함하고, 보통의 class와는 몇가지 차이점이 있다.
- Fields는 value가 할당되어야 한다.(Fields must be assigned a value)
- Fields는 public static final로 간주된다.(Fields are considered public static final)
- Public static과 final 변경자는 명시적으로 쓸 필요는 없다. (Public static and final modifiers don't need to be explicitly stated)
public interface MyInterface {
int MY_CONSTANT = 9;
}
MY_CONSTANT field는 명시적으로 쓰여있지 않아도 public static final인 상수로 구성되어있다.
Abstract methods
interface의 메소드는 class method와 다른점이 있다.
- method의 body를 가지지 않는다.(Methods don't have bodies)
- Implementation는 interface를 구현하는 클래스에서 제공된다. (Implementation is provided by classes implementing the interface)
- 명시적으로 쓰여 있지 않아도 Method는 public abstract으로 간주된다. (Methods are considered public abstract even if not explicitly stated)
- abstract와 final로 될 수 없고 유효하지 않은 조합이다. (Cannot be final as abstract and final is an invalid combination)
public interface MyInterface {
int doSomething();
String doSomethingCompletelyDifferent();
}
Nesting
Java 1.1 에서는 class 안에 class 개념을 도입했다.
static과 non-static 형태로 제공된다. interface에는 다른 interface와 class가 포함될 수 있다.
명시적으로 씌여있지 않더라도 interface와 class는 public static으로 간주된다.
public interface MyInterface {
class MyClass {
//...
}
interface MyOtherInterface {
//...
}
}
Enums과 Annotations
Java5 에서 Enums과 Annotations가 추가되었다. 이 두가지 유형은 interface안에 쓰여질 수 있다.
Generics
Java 5에서는 Generics이 추가되었는데 generics은 명시적인 type 대신에 used type parameter를 허용한다.
이 방법으로 코드를 짜면, type safety를 지키면서 다양한 type의 코드와 함께 작동되거나 각 개별 type 에 대한 구현(implementation) 을 제공하는 코드를 작성할 수 있다.
Java 5 에서의 interface는 type parameter를 정의(define)한 다음, method의 return type 이나 method parameter type 대신에 사용할 수 있다.
interface Box<T> {
void insert(T item);
}
class ShoeBox implements Box<Shoe> {
public void insert(Shoe item) {
//...
}
}
Static methods
Java 8 부터는 interface에 static method를 포함할 수 있다.
원래 interface의 method는 모두 abstract 이었고 implementation(구현체)은 없었다. implementation은 interface를 구현하는 class에 책임이 있었다.
interface의 static method는 method의 body의 implementation도 제공되어야 했다.
이런 method를 포함하기 위해서 보통 static 키워드를 사용하고 static method는 기본적으로 public으로 간주한다.
public interface MyInterface {
// This works
static int foo() {
return 0;
}
// This does not work,
// static methods in interfaces need body
static int bar();
}
Static method inheritance
일반적인 static method와 달리 interface의 static method는 상속되지 않는다.
즉, 이것은 method를 호출하려면 구현하는 클래스 대신에 interface를 바로 접근해서 호출해야한다.
MyInterface.staticMethod();
이 동작은 다중 상속 문제를 방지하는 데 매우 유용하다. 두 개의 interface를 구현하는 클래스가 있다고 가정 해 보자.
각 interface에는 동일한 이름과 signature을 가진 static method가 있을 때 어떤 것이 더 우선해야 할까?
Why is it useful
interface를 구현한 클래스에 helper methods와 수많은 interface가 있다고 상상해보자.
interface 외에도 interface에 속한 static method를 가진 유틸리티 클래스가 있다.
예제는 JDK에서 직접 찾을 수 있다. 인터페이스 java.util.Collection 및 해당 유틸리티 클래스 java.util.Collections.
인터페이스의 정적 메소드의 경우 이 접근법은 더 이상 필요 없으며 권장되지 않는다. 한 곳에서 모든 것을 함께 가질 수 있다.
Default methods
Default methods는 method body를 제공해야하는 방식으로 static method와 유사하다. Default methods는 단순히 default 키워드만 붙여주면 된다.
static method와는 달리 Default methods는 인터페이스를 구현하는 클래스에 상속된다. 중요한 것은 구현 클래스가 인터페이스에서 제공하는 기본 behavior를 무시할 수 있다는 것이다.
그러나 한 가지 예외가 있는데 Object의 toString, equals 및 hashCode와 같은 signature로 인터페이스의 Default methods를 사용할 수 없다.
Why is it useful
인터페이스에서 직접 구현하는 것은 좋지 않다. 그렇다면 이 기능이 처음 도입 된 이유는 뭘까?
인터페이스에는 한 가지 문제가 있는데 일단 인터페이스를 다른 사람들에게 노출시키면 인터페이스를 변경하기가 매우 어렵다.
Java는 전통적으로 하위 호환성을 매우 중요하게 생각한다. Default methods는 기존 인터페이스를 새로운 메소드로 확장하는 방법을 제공한다.
중요한 것은 Default methods가 이미 구현을 제공한다는 것이다.
즉, 인터페이스를 구현하는 클래스는 새로운 메소드를 구현할 필요가 없으면서 여전히 호환 가능하다.
물론 구현이 적합하지 않은 경우 나중에 기본 메서드를 재정의 할 수도 있다.
간단히 말해 binary 호환성을 유지하면서 인터페이스를 구현하는 기존 클래스에 새로운 기능을 제공 할 수 있다.
Conflicts
두 개의 인터페이스를 구현하는 하나의 클래스가 있다고 가정해 보자. 이 인터페이스에는 동일한 이름과 signature을 가진 Default methods가 있다.
interface A {
default int doSomething() {
return 0;
}
}
interface B {
default int doSomething() {
return 42;
}
}
class MyClass implements A, B {
}
이제 동일한 signature을 사용하는 동일한 Default methods가 두 개의 다른 인터페이스에서 상속되고 각 인터페이스에는 다른 구현이 있다.
그렇다면 두 구현체 중 어떤 것을 사용해야하는지 클래스가 알 수 있을까?
알 수 없다. 위의 코드는 컴파일 에러가 난다. 제대로 동작되게 하려면 클래스에서 conflict난 메서드를 override 해야한다.
Private methods
Java 8과 default 및 static 메소드의 도입으로 인터페이스는 갑자기 메소드 signatures뿐만 아니라 implementation(구현)을 포함 할 수 있다.
그러한 구현을 작성할 때 몇 가지 간단한 메소드 중에서 더 복잡한 메소드를 작성하는 것이 좋다. 이러한 코드는 재사용, 유지 및 이해하기가 더 쉽다.
일반적으로 이러한 메소드는 구현 세부 사항이며 외부에서 볼 수 없고 사용할 수 없으므로 주로 private methods를 사용한다.
그러나 Java 8에서는 인터페이스에 private methods를 사용할 수 없다. 즉, 다음 중 하나를 수행 할 수 있다.
1. 길고 복잡하며 어려운 method body를 사용할것
2. 인터페이스의 일부인 helper methods를 사용할 것. 이렇게 하면 캡슐화가 중단되고 interface와 구현 class의 public API가 오염된다.
다행히 Java 9부터는 인터페이스에서 private methods를 사용할 수 있다.
다음과 같은 특성을 가지고 있다. :
- 메소드 body가 있고 abstract이 아니다.
- static 이거나 non-static 일 수 있다.
- 구현 클래스와 인터페이스가 상속되지 않는다.
- 인터페이스에서 다른 메소드를 호출 할 수있다.
private는 private, abstract, default 또는 static 메소드를 호출 할 수 있다.
private static은 static 및 static private 메소드만 호출 할 수 있다.
public interface MyInterface {
private static int staticMethod() {
return 42;
}
private int nonStaticMethod() {
return 0;
}
}
Chronological order
Here is a chronological list of the changes by Java version:
Java 1.1
- Nested classes
- Nested interfaces
Java 5
- Generics
- Nested enums
- Nested annotations
Java 8
- Default methods
- Static methods
Java 9
- Private methods
'backend > Java' 카테고리의 다른 글
[Java] Java Iterator를 Java Stream 으로 변환하기 (1) | 2018.09.07 |
---|---|
throw new UnsupportedOperationException() (4) | 2009.10.30 |
[Effective Java] 23. 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자. (0) | 2009.10.30 |
[Effective Java] 19. 타입을 정의할 때만 인터페이스를 사용하자. (2) | 2009.10.27 |
[Effective Java] 15. 가변성을 최소화하자. (2) | 2009.10.19 |
[Effective Java] 11. clone 메소드는 신중하게 오버라이드 하자. (2) | 2009.10.15 |
[Effective Java] 9. equals 메소드를 오버라이드 할 땐 hashCode 메소드도 항상 오버라이드 하자. (0) | 2009.09.28 |
[Effective Java] 8. equals 메소드를 오버라이딩 할 때는 보편적 계약을 따르자. (4) | 2009.09.24 |