𝗪𝗵𝘆 𝗶𝘀 𝗠𝗮𝗽 𝗡𝗼𝘁 𝗜𝘁𝗲𝗿𝗮𝗯𝗹𝗲 𝗶𝗻 𝗝𝗮𝘃𝗮?— Most Asked Interv

“Why is Map not directly Iterable in Java?”

blog.stackademic.com

 

왜 Map은 반복 가능하지 않은가?

이 글에서는 Map의 반복 가능하지 않도록 설계된 이유에 대해서 말합니다.
이를 확장해 class, interface 상속에 대한 내용도 추가해 놓았습니다.


Java Collection 프레임워크의 구조를 보면 알 수 있듯이, MapCollection이 아니며 Iterable 또한 상속받지 않습니다.
(Iterable 을 상속의 의미는 Enhanced For Loop 를 사용할 수 있음을 말합니다)

위의 글에서는 왜 "MapIterable 하지 않은가?" 에 대해 말합니다.
주된 주장은 MapCollection, 즉 요소의 집합이 아니며 Pair의 묶음이라는 것 입니다.
Map은 기본적으로 key-value 형태의 쌍이기 때문에, 명시하지 않는다면 어떤 원소값의 반복인지 알 수 없습니다.

  1. key 값을 반복
  2. value를 반복
  3. key-value 쌍을 반복

이렇게 다양한 옵션들이 있기 때문에 정확히 무엇을 순회할건지를 알 수 없는 모호함이 발생한다는 것으로
정확히 어떤것을 순회할지를 나타낸 후에야 Iterable해지는 것을 알 수 있습니다.

Map<String, Integer> scores = new HashMap<>();
scores.put("Madhavi", 90);
scores.put("Kiran", 85);
scores.put("Anita", 88);

// Iterate over entrySet
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Iterate over keys
for (String key : scores.keySet()) {
    System.out.println("Key: " + key);
}
// Iterate over values
for (Integer value : scores.values()) {
    System.out.println("Value: " + value);
}

class를 여러개 상속 받을 수 없는 비슷한 이유

이것과 비슷한 개념으로 class의 상속이 존재하는데요
class는 기본적으로 상태를 가지게 됩니다.

상태를 가진다는 것은 Field 값이 존재한다는 것이고, method 를 통해 이를 조작하게 됩니다.
재밌는 상상을 해볼까요?

만약 dragon, bird 두 개의 클래스를 상속받는다 가정하고
각각이 fly() 라는 메서드와 ,wings 라는 필드가 존재한다고 하겠습니다.

class Bird{

    int wings;

    public void fly(){
        // do something
    }

}

class Dragon{
    int wings;

    public void fly(){
        // do something
    }
}

class bat extends Dragon, Bird{
    // 누구의 날개를 사용해서 어떻게 날 것인가?
    // error 
}

이를 상속받은 클래스에서는 과연 누구의 wings를 가지고 어떻게 fly() 연산을 수행해야 할까요?
이렇게 직접적인 구현을 가지고 있는 클래스는 여러개를 상속받게 하는 순간 모호함에서 오는 이상이 발생할 수 있기 때문에
자바에서는 원칙적으로 막아두는 것 입니다.

반면에 interface는 상태를 가지지않고, 그저 어떤 연산이 가능한지만 명시해놓는 명세서입니다.
따라서 여러개를 상속받아도 모호함에서 오는 이상이 없는 것 이죠

그렇기 때문에 interface의 경우는 여러개를 상속받는 것을 막아두지 않습니다.

Why Abstraction??

우리는 이전의 글을 통해 Abstraction의 개념을 간략하게 이해했습니다.

 

Abstraction(추상화) 기본 개념 - 1편

Abstraction(추상화)란? 자바에서는 추상클래스 , 추상메서드 , 추상화 등 "추상"이라는 말이 자주 쓰입니다. 또한 Abstract으로 선언된 클래스 , 메서드 등을 보고 추상화시켰다 합니다. 그렇다면 추상

masiljangajji-coding.tistory.com

추가적으로 다음 글을 읽는 것을 추천드립니다.

 

Specification 기본 개념 및 활용

Specification 프로그램에서 터지는 버그는 대부분 동작에 대한 오해로 발생합니다. 이러한 동작의 오해를 줄이기 위한 대표적인 방법으로 Spec(명세)이 존재합니다. 또한 완성된 프로그램은 필수적

masiljangajji-coding.tistory.com

 

이 글에서는 이전 글과 비슷한 내용을 다루되 더욱 세세한 부분을 보려고 합니다.


Abstraction을 하게되면 얻을 수 있는 핵심 이점은 다음과 같습니다.

  1. 시스템을 구성 요소 또는 모듈로 나누어 재사용할 수 있다록 한다.(Modularity 모듈성)
  2. 모듈 주의에 벽을 구축하여 모듈이 자체적으로 책임지고 시스템의 다른 부분에서 발생한 버그가 모듈의 무결성을 손상시킬 수 없도록 한다.(Encapsulation 캡슐화)
  3. 모듈의 구현 세부사항을 숨겨 세부사항을 변경해도 시스템의 나머지 부분을 변경하지 않아도 된다. (Imformation Hiding)
  4. 기능을 모듈의 책임으로 만들어 여러 모듈에 걸쳐 분산되지 않도록 한다. (Separation of Concerns 관심사분리)

어떻게 이런 일이 가능한 것일까요??

 

자바에서 제공하는 List에 대해서 보겠습니다.

ArrayList , LinkedList 둘 다 List Interface를 상속받아 구현하는 방식으로 설계돼있습니다.

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList

 

 

지금부터 이러한 구조의 이점을 알아보겠습니다.

 

List Interface

List라는 타입이 필수적으로 가져야 하는 연산을 명시한 것입니다.
연산에대한 간단한 Specification을 명시한 것이죠

 

따라서 우리는 앞으로 List라는 타입이 가져야하는 연산을 알 수 있습니다.

(List에 명시돼있는 size , isEmpty , contains 연산의 Specification)

 

사용자는 List의 구현체를 보지 않아도 , 즉 내부적인 구현이 어떤식으로 이루어져있는지 몰라도
간단한 Spec만으로 충분한 사용이 가능해집니다. (Imformation Hiding)

 

사용자가 List 내부구현에 의존하지 않고 사용하기 떄문에 갑작스럽게 List 구현체의 내부구현을 바꾸더라도
아무런 문제가 발생하지 않을 것 입니다. (Encapsulation)

 

Spec을 명시함으로써 자연스럽게 연산의 구조를 알 수 있게됐고 이를통해 세세한 메서드 분리가 가능해집니다.
이는 PreCondition , PostCondition 같은 조건들을 제공하고 Unit Test를 돕습니다.

 

후에 배열구조나 노드구조를 사용하지 않는 새로운 구조의 List를 만든다고 했을때도
List Interface를 재사용 함으로써 코드의 재사용성을 증가시킬 것 입니다. (Modularity)

 

List이외에도 Collection FrameWork 안에있는 Abstract Data Type은 Interface를 두고 구현체를 따로 두는 방식을 채택합니다.

 

 

이러한 방법은 쓸대없는 일을 여러번 하는 것 처럼 보이지만 실제로는 위에서 기술 한 것처럼 많은 이점이 있는것이죠

 

Collection 의 Super Type으로 Iterable가 존재하는 것도 같은 이치입니다.

 

계속해서 강조하자면 Abstraction은 매우 큰 범위를 갖고있는 말입니다.

 

사용자에게 내부구현 정보를 숨기는 것 ... Abstraction

새로운 구조를 갖는 List가 필요할때 implements 받아 연산 정의 절차를 줄이는 것 ... Abstraction

 

무언가를 "간추린다" 면 전부다 Abstraction에 해당하기 떄문이죠 

 

이 글에서는 Abstraction이 갖는 이점과 보다 세세한 관점에 대해서 다뤘습니다.
도움이 되셨다면 좋겠습니다.

Abstract Class Vs Interface

Abstract Class(추상클래스) 와 Interface(인터페이스)는 어떤 차이가 있을까요?

기능적인 차이점 부터 알아보겠습니다.

추상클래스

  1. 타입에 제한없는 변수
  2. 메서드 제한 없음
  3. extend KeyWord로 상속
  4. 다중 상속 불가능

인터페이스

  1. static final 상수 변수
  2. abstract method , default method
  3. implements KeyWord로 상속
  4. 다중 상속 가능

보다 다양한 차이점이 있지만 크게 4가지만 간추렸습니다.

 

여기서 중요한것은 단순히 기능적인 차이점이 아니라 어쨰서? 입니다

 

왜 이런 기능적인 차이가 있는걸까요?

지금부터 왜? 에 대해서 알아보겠습니다.

 

(이 글은 클래스의 Implementation과 Abstraction 개념을 알고있다는전제하에 작성된 글입니다.

따라서 관련된 내용이 궁금하신 분들은 다음을 참고해 주세요)

 

Abstraction(추상화) 기본 개념 - 1편

Abstraction(추상화)란? 자바에서는 추상클래스 , 추상메서드 , 추상화 등 "추상"이라는 말이 자주 쓰입니다. 또한 Abstract으로 선언된 클래스 , 메서드 등을 보고 추상화시켰다 합니다. 그렇다면 추상

masiljangajji-coding.tistory.com

 

클래스 설계와 Abstraction Barrier

클래스란 클래스란 특정한 타입을 지정하는것을 의미합니다. 우리는 이미 자바에서 제공하는 Primitive Type을 사용하고있습니다. (byte,short,int,long,folat,double,boolean,char) 이 Type은 각각 특정한 목적을

masiljangajji-coding.tistory.com

 

추상클래스 Vs 클래스

추상클래스는 기본적으로 클래스이기 떄문에 Implementation을 갖습니다.

또한 일반적인 클래스와 같이 특정한 타입을 정의하기 위해서 사용됩니다.

그럼 일반 클래스와 어떤 점이 다를까요?

일반 클래스와 차이점은 단 하나입니다. 이 타입이 필요한 연산을 구현하지는 않고 명시만 하여 간추린 것이죠

자바에서는 이를 Abstract method 라고 지칭합니다.

 

좀더 포괄적으로 이해하면 다음과 같습니다.

  • 일반클래스 : 이 타입은 Implementation을 갖고 이런 연산을 수행해 !
  • 추상클래스 : 이 타입은 Implementation을 갖고 이런 연산을 할수는 있어야 해 !

추상클래스는 미완성 설계도 , 미완성된 클래스 라고 이해하면 됩니다.

구현하지 않고 명시만 해놓은 연산을 extends받는 클래스에서 구현하여 추상클래스의 설계도를 완성시키는 것이죠

이점 외에는 클래스와 동일하기 때문에 당연히 변수와 메서드에 제한이 없습니다.

 

하지만 이것만으로 해결이 안되는 부분이 존재합니다 . 바로 다중상속이 불가능한 것입니다.

왜 클래스는 다중상속이 불가능 할까요??

이를 알아보겠습니다.

class Sparrow {
    int wing;
    int beak;
    int feather;
}

class Pigeon {
    int wing;
    int beak;
    int feather;
}

참새와 비둘기 타입을 정의했습니다.

참새와 비둘기 타입 모두 날개,부리,깃털을 가집니다.

또한 이것들은 각각의 타입에 대한 Implementation에 속합니다.

참새,비둘기 모두 Implementation을 갖기 떄문에 이 둘을 상속받는다면

두 클래스의 Implementation을 지켜줘야 합니다.

그렇다면 상속받은 클래스에서 사용되는 날개는 어떤 클래스의 날개일까요?


마찬가지로 상속받은 클래스에서 사용되는 깃털은 어떤 클래스의 깃털인가요??

또 부리를 이용해 불을 내뿜는 Dragon타입을 추가적으로 상속받는다면 어떤 클래스의 부리를 이용해 불을 내뿜을 건가요??

 

이런식으로 Implementation끼리의 충돌이 발생할 수 있습니다.

따라서 클래스는 원천적으로 다중상속이 불가능합니다.

그렇기 떄문에 상속할때도 extends(확장)라는 KeyWord를 사용합니다.

기존 클래스의 Implementation은 지키면서 추가적으로 살을 덧붙여 확장시켜 사용하라는 의미입니다.

동일한 논리로 미완성이기 댸문에 new를 통한 객체생성도 불가능 합니다.

 

인터페이스 Vs 추상클래스

앞서 말했듯 추상클래스는 Implementation과 연산에 대한 명시가 돼있는 클래스를 의미합니다.

그런데 이런 경우에는 어떨까요?

추상클래스의 추상화 정도가 점점 높아져 어떠한 필드변수도 갖지않고 연산에대한 명시만 돼있는 것입니다.

abstract class Dragon {

    abstract void fly(int wing);

    abstract void breath(int beak);

}

이 Dragon 추상클래스는 날수있는 기능과 불을뿜는 기능을 명시해놓은 명세서입니다.

별도의 Implementation을 갖지않는 클래스기 떄문에 다중상속이 되어도 문제가 발생하지 않습니다.

하지만 여전히 클래스의 범주에 속하기 떄문에 다중상속이 불가능합니다.

 

그래서 나온것이 바로 Interface입니다.

 

추상클래스의 추상화정도가 계속해서 증가하여 필드변수를 갖지않고 기능에대한 명시만 있을때는
다중상속의 문제점이 사라지기 떄문에 Interface라 지칭하고 다중상속이 가능하게끔 만든 것 입니다.

 

따라서 인터페이스는 Implementation을 갖지 않고 다중상속이 되어도 문제가없는 상수값과 기능에대한 명시만 가집니다.

 

같은 논리로 상속의 KeyWord도 extends가 아닌 implements(구현)을 사용합니다. 그럼 default method는 무엇인가요??

이것또한 인터페이스의 등장과 동일합니다.

인터페이스를 사용하다보니 어라?? 이런 연산은 다중상속되도 문제가 없을 것 같은데?? 라고 하는것을

default method로 만들어 놓은 것입니다.

 

이렇게 되면 해당 인터페이스를 구현한 클래스들이 그 메서드를 구현하지 않아도 되는 장점이 있습니다.

 

자바는 언어가 설계될떄 모든 연산을 클래스를 통해 해결이 가능하다 믿었습니다.
하지만 프로그래밍이 고도화 됨에 따라 요구사항이 증가했고 기존에 사용하던 클래스만으로는 극복불가능한 문제점들이 생기게 됐습니다.

 

따라서 클래스라는 타입이 갖는 태생적인 한계를 극복하기 위해 여러가지 기능을 덧붙여서 만들었다고 보면 될 것 같습니다.

 

도움이 되셨다면 좋겠습니다.

+ Recent posts