자바 유료화 관련해서 궁금해서 알아보던중 좋은 글을 작성해주신 분들이 있어서 공유합니다.

결론적으로 개인 개발환경에서 Oracle JDK를 사용하는건 상관없고 솔루션같이 상업적으로 사용 되는 부분에서는 OpenJDK를 사용하면 된다는 건가...

아직도 잘 모르겠다. 시간이 지나면 답이 나오겠지.



Java8의 Stream에 map 기능을 사용하다가 이런문제를 겪었다.



Iterable과 Iterator 정확한 정리를 하지 않고 무턱대고 사용하다보니 발생한 문제였다.

정확하게 집고 넘어가기 위해 정리해보자.

Iterator

Iterator는 자바 1.2에 발표된 인터페이스이다.  hasNext, next 등을 통해 현재 위치를 알 수 있고 다음 element가 있는지를 판단하는 기능등에 대한 명세를 제공한다. 이를 사용하기 위해서는 Iterator 인터페이스의 내용을 직접 구현해야 한다. 

대게 Collection 인터페이스를 사용하는 클래스의 경우 별도의 Iterator를 구현하여 사용하고 있다. 밑에 Iterable을 설명하면서 정리해보자.

1
2
3
4
5
6
7
8
9
10
11
12
public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    default void forEachRemaining(Consumer<super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
cs


Iterable

Java 1.5부터 나온 인터페이스로 Iterator보다 더 늦게 나온 인터페이스로 Iterator를 제공하는 메서드를 보유하고 있는 인터페이스이다.

이 인터페이스는 실질적으로 for-each를 사용할 수 있는 클래스라는것을 명세해주는 기능을 제공하고, Iterable을 상속받은 클래스는 Iterator를 사용하여 for- each 기능을 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Iterable<T> {
   Iterator<T> iterator();
    
   default void forEach(Consumer<super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
 
   default Spliterator<T> spliterator() {
       return Spliterators.spliteratorUnknownSize(iterator(), 0);
   }
}
cs

조금 더 이해가 가능하도록 ArrayList를 예로 들어보자.

ArrayList는 List를 구현하고 있고, List는 Collection을 상속받고 있으며 Collection은 Iterable을 상속받고 있다.

1
2
3
4
5
6
7
// ArrayList
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
// List
public interface List<E> extends Collection<E> {
// Collection
public interface Collection<E> extends Iterable<E> {
cs


그래서 ArrayList에 보면 Iterator<E> iterator()가 구현되어 있다.

그리고 Iterator()에서 반환하는 Iterator인터페이스를 구현한 Itr 클래스도 제공하고 있으며 이를 사용하여 반복 동작을 가능하도록 사용한다.

※Java 1.8 부터는 Iterable에 forEach default method를 제공하고 있어서 바로 사용할 수 있다.


결론은
for-each 기능을 제공하는 Collection의 경우 Iterable을 구현하고 있으며, 그 Iterable 인터페이스에 있는 Iterator<E> iterator() 메소드를 구현하여 Collection의 요소들은 Iterate 하는데 사용된다.


static 메소드를 자기고 있는 클래스를 상속받은 자식 클래스에서 그 static 메소드를 override 할 수 있을까?

안될거 알지만 한번 확인해보고 싶었다.

먼저 static method를 가지고 있는 Parent을 만들었다.


1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 부모 클래스
 */
static class Parent {
    public static void getData() {
        System.out.println("부모 getData");
    }
 
    public void method() {
        System.out.println("부모 method");
    }
}
cs


그리고 이를 상속하는 Child 클래스를 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 자식 클래스
 */
static class Child extends Parent {
    public static  void getData() {
        System.out.println("자식 getData");
    }
 
    public void method() {
        System.out.println("자식 method");
    }
}
cs


그리고 실행시켜보자.

1
2
3
4
5
6
7
8
9
// 부모클래스
Parent c1 = new Parent();
c1.getData();
c1.method();
 
// 자식 클래스
Parent c2 = new Child();
c2.getData();
c2.method();
cs


만약 정상적으로 상속이 되었을 때 우리가 원하는 결과는 이렇다.

예상 결과

부모 getData
부모 method
자식 getData
자식 method

하지만 실제 결과는 다음과 같다.

실제결과
부모 getData
부모 method
부모 getData
자식 method

왜냐하면 static method는 상속이 되지 않기 때문이다. 왜냐면 static 메서드는 클래스가 컴파일 되는 시점에 결정이 되지만 Override에 경우에는 런타임 시점에 사용될 메서드가 결정이 된다. 그래서 애초에 성립하기 어렵다.


그리고 애초에 static에 경우 클래스단위로 만들어지기 때문에 객체 단위로 형성되는 Override 성립될 수 없다.
이런 문제를 방지하기 위해서는 재정의 하기 위해서는 무조건 @Override를 붙혀 주자. 

@Override만 붙여주어도 이렇게 바로 문제가 된다는 것을 확인 할 수 있다.


참고사항

https://docs.oracle.com/javase/tutorial/java/IandI/override.html

https://blog.naver.com/gngh0101/221206214829


+ Recent posts