728x90


람다식의 개념 및 기본 문법

람다식이란 ?

 

람다식(Lambda Expression)은 자바 8부터 도입된 함수형 프로그래밍의 개념입니다. 람다식은 간단히 말하면 익명 함수(anonymous function)를 나타내는 것으로, 메서드를 하나의 식으로 표현하는 방법입니다. 기능이 비워져 있는 상태에서 기능을 추가해서 사용할 수 있도록 하는 것이 인터페이스 속에 있는 추상메서드이며, 그 메서드에 기능을 부여(정의)하여 사용하는 것이 람다식의 핵심입니다. 코드의 간결화 및 병렬처리에 효율적입니다. (Collection API 성능을 효과적으로 개선(Stream))

객체지향 프로그래밍은 프로그램을 객체들의 모임으로 구성하는 방식입니다. 객체는 데이터와 그 데이터를 처리하는 메서드로 이루어져 있으며, 객체들 간의 상호작용을 통해 프로그램이 동작합니다. 객체지향 프로그래밍에서는 데이터와 해당 데이터를 처리하는 메서드가 논리적으로 묶여있어 유지보수와 확장이 용이하며, 코드의 재사용성을 높일 수 있습니다.

반면, 함수형 프로그래밍은 함수를 일급 객체로 취급하고, 함수의 조합을 통해 프로그램을 작성하는 방식입니다. 함수형 프로그래밍은 상태 변경이나 가변 데이터보다는 불변성과 순수 함수(pure function)의 개념을 강조합니다. 순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며, 부작용(side effect)을 발생시키지 않는 함수를 말합니다.

람다식은 함수형 프로그래밍에서 함수를 간결하게 표현하기 위한 방법 중 하나입니다. 람다식은 익명 함수를 나타내는데, 함수형 프로그래밍에서 함수를 일급 객체로 다루는 특성과 잘 어울립니다. 람다식을 사용하면 함수를 변수에 할당하거나 매개변수로 전달할 수 있으며, 함수형 인터페이스를 구현하는 데 유용하게 사용됩니다.

람다식은 주로 함수형 인터페이스(functional interface)를 구현하기 위해 사용됩니다. 함수형 인터페이스는 단 하나의 추상 메서드만을 가지는 인터페이스를 말하며, 람다식은 이 추상 메서드의 구현을 직접 전달하는 방식으로 사용됩니다.

  • 클래스 바깥에 올 수 있는 것은 패키지, 임포트, 외부클래스 내부에 올 수 있는 것은 필드, 메서드, 생성자, 이너클래스

    람다식 이해를 위한 기본 용어의 정리
    1. 함수(function) : 기능, 동작을 정의합니다.
      void(리턴타입) abc(메서드 이름)(입력매개변수) {함수
                     //중괄호 안에 기능 및 동작을 입력
      }

    2. 메서드(method) : 클래스 또는 인터페이스 내부에서 정의된 함수 (=메서드)
      class A {
               void abc() {  → class 내부에서만 존재가능한 함수는 메서드
                          //기능 및 동작
                     }
      }

    3. 함수형 인터페이스(functional interface) : 내부에 단 1개의 추상메서드만 존재하는 인터페이스
      interface A {
               public abstract void abc();
      }

      인터페이스 내부에 2개 이상의 메서드가 담겨있다면 , 람다식으로 함수를 생략하여 작성하게 되면 어떤 메서드가 생략된 후 호출되는지 구분되어지기 않기 때문에 꼭 1개의 추상메서드만 담겨있는 인터페이스만 람다식으로 표현이 가능합니다.

 

 

  1. 함수적 프로그래밍에서의 함수 사용 (자주 사용하는 기능을 구현) : 함수를 정의하고 바로 호출할 수 있습니다.

  2. 객체 지향형 프로그래밍에서의 메서드(함수=기능)사용 : 인터페이스 or 클래스 내의 메서드 정의 및 구현 후 타입 참조변수로 객체를 생성 한 후 타입 참조변수를 이용해서 객체 내부의 메서드를 호출 할 수 있습니다.
            package jungsuk_0624;

            //추상메서드를 담고 있는 인터페이스를 정의
            interface A {
                void abc();
            }


            //인터페이스를 구현한 자식클래스이기 때문에 미완성 메서드를 구현할 의무가 있음
            class B implements A {
            
                @Override
                public void abc() {
                    System.out.println("메서드 내용 : 자식클래스로 객체를 생성한 후 A 인터페이스 안에 정의된 메서드 호출");
                }
            }


            public class OOPvsFP_01 {
            
                public static void main(String[] args) {

                    // #1. 객체지향 프로그래밍 문법(OOP에서 메서드를 생성)
                    // 클래스 속에 있는 모든 메서드를 호출하기 위해서는 객체를 생성해야 함
                    
                    A a1 = new B();
                    a1.abc();

                    // #2. 객체 지향 프로그래밍 문법 -> 익명이너클래스 사용 방법
                    // 자식 클래스를 컴파일러에게 만들어 달라고 한 후 객체를 사용
                    
                    A a2 = new A() {
                        @Override
                        public void abc() {
                            System.out.println("메서드 내용 : 익명 이너클래스 사용하여 호출");
                        }
                    };
                    
                    a2.abc();


                    //#3. 함수적 프로그래밍 문법(람다식) : 익명 이너클래스를 축약
                    A a3 = () -> {System.out.println("메서드 내용 : 람다식으로 정의된 메서드 호출");};
                    a3.abc();
                }

            }

 

 


📋 람다식의 활용

 

🎈람다식의 3가지 활용

 

  1. 익명 이너 클래스 내 구현 메서드의 약식(람다식) 표현 (함수적 인터페이스만 가능)
    • 함수형 인터페이스는 하나의 추상 메서드만을 가지는 인터페이스를 말합니다. 예를 들어, Runnable, Comparator, ActionListener 등이 함수형 인터페이스의 예입니다.
    • 람다식은 함수형 인터페이스의 추상 메서드를 간결하게 구현하는 방법입니다. 람다식은 매개변수 목록, 화살표(>), 그리고 메서드 구현부로 이루어집니다.

  2. 메서드 참조 (인스턴스 메서드 참조 Type1, 정적 메서드 참조, 인스턴스 메서드 참조 Type2)
    ① 인스턴스 메서드 참조 Type 1 → 이미 정의된 인스턴스 메서드 참조
    인스턴스 메서드 참조를 사용하면 기존 클래스의 메서드를 함수형 인터페이스에 매우 간결하게 매핑할 수 있으며, 가독성을 향상시킬 수 있습니다. 여기서 인스턴스는 메서드를 호출할 객체의 인스턴스를 의미하고, 메서드는 호출할 메서드의 이름을 의미합니다.

    인스턴스 메서드 참조는 주로 함수형 인터페이스의 추상 메서드와 동일한 시그니처(매개변수 타입 및 반환 타입)를 가지는 인스턴스 메서드를 참조할 때 사용됩니다. 리턴타입과 매개변수가 동일해야한다는 의미입니다.

    인스턴스 메서드 참조 문법 → 인터페이스 이름 인터페이스 변수 = 인스턴스(클래스 객체이름 ) :: 인스턴스 메서드 이름

    ② 정적 메서드 참조 Type 1 → 이미 정의된 정적 메서드 참조
    정적 메서드는 객체 생성을 하지 않아도 되기 때문에 앞서 본 인스턴스 메서드 참조와 문법이 조금 다릅니다.
    정적 메서드 참조 문법 → 인터페이스 이름 인터페이스 변수 = 클래스 이름 :: 정적 메서드 이름

    ③ 인스턴스 메서드 참조 Type 2 → 첫번째 매개변수로 전달된 객체 메서드를 참조하는 경우
    정적 메서드 참조 문법 → 인터페이스 이름 인터페이스 변수 = 클래스 이름 :: 인스턴스 메서드 이름

  3. 생성자 참조

    ① 배열 생성자 참조 (Array Constructor Reference) : 배열 생성자 참조는 배열을 생성하는 데 사용되는 생성자를 참조하는 것입니다

    ② 클래스 생성자 참조 (Class Constructor Reference): 클래스 생성자 참조는 클래스의 생성자를 참조하는 것입니다. 클래스의 new 생성자를 참조하는 경우 → interface 메서드의 리턴타입 = 클래스 객체
    배열의 new 생성자를 참조하는 경우 → interface 메서드의 리턴타입 = 배열 객체 타입[]::new
728x90
반응형
728x90

3. Map<K, V>

키(key)와 값(value)의 쌍으로 데이터를 저장하는 인터페이스입니다. 키는 중복될 수 없고, 값은 중복이 가능합니다. List의 index처럼 Map의 key를 가지고 데이터에 접근하기 때문에 중복이 될 수 없습니다. 대표적인 클래스로는 HashMap, TreeMap, LinkedHashMap, HashTable 등이 있습니다.

Collection과는 별개의 interface입니다 ( List, Set과 기본 메서드가 다릅니다.) Key-Value 한 쌍을 Entry라고 부릅니다. 실제로 엔트리를 저장하는 type도 Entry로 저장되어 있는데, Map의 이너로 저장되어 있어 Map.Entry로 한 쌍의 데이터를 저장할 수 있습니다.

https://velog.io/@mmy789/Java-컬렉션-프레임워크-3

 

  • Map<K, V> 컬렉션의 특징
    • 키-값 쌍 저장: Map은 키와 값의 쌍을 저장합니다. 각 키는 유일해야 하며, 중복된 키의 저장을 허용하지 않습니다. 따라서 키를 기반으로 값을 검색하거나 업데이트할 수 있습니다.

    • 순서 보장 여부: 일반적으로 Map은 저장 순서를 보장하지 않습니다. 데이터를 저장한 순서대로 순회할 수 없으며, 순서에 의존하는 작업을 수행할 때는 LinkedHashMap을 사용하는 것이 좋습니다. 그러나 자바 8부터는 LinkedHashMap의 일부 메소드에 순서를 보장하는 기능을 추가하여 순서를 보장할 수도 있습니다.

    • 허용하는 키와 값의 종류: Map은 null 값을 키와 값으로 사용할 수 있습니다. 또한, 일부 구현체에서는 키와 값의 타입에 제한을 두지 않고 모든 종류의 객체를 사용할 수 있습니다.

    • 효율적인 검색과 업데이트: Map은 키를 기반으로 값을 검색하는데 매우 효율적입니다. 내부적으로 해시 맵(HashMap)이 가장 일반적으로 사용되며, 해시 함수를 이용하여 키의 해시코드를 생성하고 배열에 저장함으로써 빠른 검색과 업데이트를 제공합니다.

    • 다양한 구현체: 자바에서는 Map 인터페이스를 구현한 여러 가지 구현체를 제공합니다. HashMap, LinkedHashMap, TreeMap 등이 있으며, 각각의 구현체는 특정한 용도에 맞게 최적화되어 있습니다. 사용하는 상황과 요구사항에 따라 적절한 구현체를 선택할 수 있습니다.

대표적인 Map<K, V> 컬렉션의 클래스

  1. HashMap <K, V> : 해시 함수를 사용하여 키를 해시코드로 변환하고, 이를 기반으로 내부 배열에 키-값 쌍을 저장합니다.
    해시 함수는 키의 해시코드를 생성하는 역할을 수행하며, 해시코드는 내부 배열의 인덱스로 사용됩니다. 저장공간을 동적으로 관리하며 디폴트값은 16이며, 원소가 16을 넘는 경우 자동으로 저장공간을 확대합니다. 이를 통해 매우 빠른 검색과 삽입 속도를 제공합니다.
    • 중복된 키를 허용하지 않음 : HashMap은 각 키는 유일해야 하며, 중복된 키의 저장을 허용하지 않습니다. 중복된 키를 사용할 경우, 기존 값은 새 값으로 대체됩니다.

    • 순서를 보장하지 않음 : HashMap은 내부 배열에 키-값 쌍을 저장하는 방식으로 동작하며, 저장 순서를 보장하지 않습니다. 입력의 순서와 출력의 순서는 동일하지 않을 수 있습니다. (key 값이 Set으로 관리) 따라서 저장된 순서에 의존하는 작업에는 LinkedHashMap을 사용하는 것이 좋습니다.

  2. HashTable<K, V> : 효율적인 데이터 구조 중 하나로, 키-값 쌍의 저장과 검색에 사용되는 자료구조입니다. 각각의 키(key)는 해시 함수를 통해 고유한 정수 값으로 매핑되고, 이 정수 값을 인덱스로 사용하여 값을 저장하거나 검색할 수 있습니다.

4. Stack<E>

 

Stack<E> 컬렉션은 Vecter<E> 클래스의 자식 클래스이며, 유일하게 클래스입니다. List<E>의 자식이기 때문에 List라고도 할 수 있기 때문에 List<E>의 기본 메서드의 사용이 가능합니다. Stack<E> 컬렉션은 제네릭(Generic) 타입으로 정의되어 있으며, E는 스택에 저장되는 요소의 타입을 의미합니다. 예를 들어, Stack<Integer>는 정수형 요소를 저장하는 스택을 나타냅니다.

Stack<E> 컬렉션은 후입선출(LIFO, Last-In-First-Out) 원칙을 따르는 데이터 구조입니다. 나중에 들어간 데이터가 가장 먼저나오는 구조입니다. 스택은 요소의 삽입과 삭제가 한쪽 끝에서만 이루어지는 선형 자료구조로, 가장 최근에 추가된 요소가 가장 먼저 제거되는 동작 방식을 가지고 있습니다. Stack<E> = Vector<E>의 기본기능 + LIFO 기능 추가 (5개 메서드) push() , peek(), pop(), serach(), empty() 추가된 5개의 메서드를 사용하기 위해서는 꼭 Stack<E> 타입으로 선언해야 합니다.

Stack<E> 컬렉션은 다음과 같은 주요한 메서드를 제공합니다:

  • push(E item): 스택의 맨 위에 요소를 추가합니다.
  • pop(): 스택의 맨 위에 있는 요소를 제거하고 반환합니다.
  • peek(): 스택의 맨 위에 있는 요소를 반환합니다. 실제로 데이터를 꺼내는 것은 아니기때문에 데이터의 변화는 없습니다.
  • empty(): 스택이 비어 있는지 여부를 확인합니다.
  • search(Object o): 지정된 요소를 스택에서 검색하고 상대적 위치(1부터 시작)를 반환합니다. 없는 요소를 출력하면 -1 을 리턴합니다.
728x90
반응형
728x90

1. Set<E>



자바 컬렉션 프레임워크(Collection Framework)에서 제공하는 인터페이스로, 중복되지 않는 요소들을 유일하게 저장하는 컬렉션입니다. 집합의 개념으로 인덱스 정보를 포함하고 있지 않고, 중복된 요소를 허용하지 않는 순서가 없는 데이터 그룹을 저장하는 인터페이스입니다. 인덱스 정보가 없기때문에 중복된 원소 중 특정 위치 값을 꺼낼 방법이 없습니다.

생성된 객체에서 데이터를 추가하고 출력할 때는 index 정보가 없기때문에 정렬 되어있는 상태가 아닌 주머니에 무작위하게 들어있기 때문에 순서가 보장되지 않습니다.(null 값 조차 한 개만 포함이 가능) 대표적인 클래스로는 HashSet, LinkedHashSet. TreeSet 등이 있습니다.

 

  • set<E> 컬렉션의 특징 : index 정보가 없기 때문에 중복저장이 불가능하기 때문에 중복저장이 가능한지 불가능한지를 판단하는 기준이 될 수 있습니다.

✅ 대표적인 Set<E> 컬렉션의 클래스


1. HashSet<E> : Set<E> 인터페이스를 구현한 클래스 중 하나로, 해시 테이블을 사용하여 요소들을 저장하는 자료구조입니다. 수집(collect)한 원소(Element)를 집합의 형태로 관리하며 저장용량( capacity - 16)을 동적 관리합니다. 입력의 순서와 출력의 순서는 동일하지 않을 수 있으며, HashSet<E>은 일반적으로 요소들의 집합을 관리하고 중복을 제거하는 데에 사용됩니다. 예를 들어, 어떤 항목들의 목록에서 중복된 항목을 제거하고 유일한 항목들을 가져와야 할 때 HashSet<E>을 사용할 수 있습니다. 또한 해시 테이블의 특성상 빠른 검색이 필요한 경우에도 유용하게 사용됩니다. ** Hash - 중복된 데이터들의 저장이 안되려면 다름과 같음을 비교해주기 때문에 중복의 확인이 필요합니다.

  • Iterator<E> (for-each로 대체 가능) : Set<E>은 index정보가 없기때문에 순서대로 요소출력이 어려운데 interator 메서드를 사용하면 set에서 컬렉션 내의 요소를 순차적으로 접근하고 삭제할 수 있는 방법을 제공합니다.
  • Set은 순서자체를 가지고 있지 않기 때문에 순서라는 것은 의미가 없지만 Iterator메서드는 모든 데이터요소에 한번씩 접근해 출력이 가능합니다. 컬렉션의 내부 구조에 상관없이 일관된 방식으로 요소에 접근할 수 있도록 합니다.
      1. hasNext() : Iterator가 다음 요소를 가지고 있는지 확인합니다. 다음 요소가 있을 경우 true를 반환하고, 없을 경우 false를 반환합니다.
      2. next() : Iterator의 현재 위치에서 다음 요소를 반환합니다. 반환된 요소는 컬렉션 내의 다음 요소로 이동합니다.
        • HashSet에서 요소의 중복 여부를 판단하기 위해 hashCode()와 equals() 메서드를 사용합니다.
          • hashCode() 메서드는 객체의 해시 코드를 반환하는 메서드입니다. HashSet은 요소를 저장할 때 요소의 hashCode() 값을 사용하여 해시 테이블에 저장하고 관리합니다.
            저장된 코드 값은 패키지명.클래스명@hashCode가 출력됩니다. 1단계로 동일한 hashCode() 값을 갖는 다른 요소들이 있다면 2단계로 equals() 메서드로 비교하여 중복 여부를 확인합니다.
            hashCode() 메서드는 객체의 내부 상태에 기반하여 정수 값을 반환하는데, 동일한 객체라면 항상 같은 hashCode() 값을 반환해야 합니다.
            HashSet에서 요소의 중복 여부를 판단하기 위해 hashCode() 값을 사용하는 이유는 해시 테이블의 성능을 향상시키기 위해서입니다. 따라서, HashSet을 사용하는 클래스에서는 hashCode() 메서드를 적절히 오버라이딩하여 동일한 객체에 대해 동일한 hashCode() 값을 반환하도록 구현해야 합니다.

          • equals() 메서드는 두 객체가 동등한지 비교하는 메서드입니다. HashSet은 요소의 중복 여부를 판단할 때 equals() 메서드를 사용합니다. HashSet에 이미 저장된 요소와 새로 추가하려는 요소를 equals() 메서드로 비교하여 중복 여부를 확인합니다. equals() 메서드가 true를 반환하는 경우 HashSet은 해당 요소를 중복된 요소로 간주하고 추가하지 않습니다.

          • Iterator<E> (for-each로 대체 가능) : Set<E>은 index정보가 없기때문에 순서대로 요소출력이 어려운데 interator 메서드를 사용하면 set에서 컬렉션 내의 요소를 순차적으로 접근하고 삭제할 수 있는 방법을 제공합니다.
             
            • Set은 순서자체를 가지고 있지 않기 때문에 순서라는 것은 의미가 없지만 Iterator메서드는 모든 데이터요소에 한번씩 접근해 출력이 가능합니다. 컬렉션의 내부 구조에 상관없이 일관된 방식으로 요소에 접근할 수 있도록 합니다.
              1. hasNext() : Iterator가 다음 요소를 가지고 있는지 확인합니다. 다음 요소가 있을 경우 true를 반환하고, 없을 경우 false를 반환합니다.
              2. next() : Iterator의 현재 위치에서 다음 요소를 반환합니다. 반환된 요소는 컬렉션 내의 다음 요소로 이동합니다.


2. TreeSet<E> : TreeSet<E>은 이진 검색 트리의 형태로 요소를 저장하며, 요소들은 정렬된 순서로 저장됩니다. 중복 요소를 허용하지 않으며, 요소들은 자동으로 정렬된 순서로 저장됩니다.
요소들은 기본적으로 오름차순으로 정렬되지만, 요소를 정렬하는 기준을 커스터마이즈할 수도 있습니다. 이진 검색 트리의 성질을 활용하여 요소의 삽입, 삭제, 검색 등의 작업을 빠르게 수행할 수 있습니다.
TreeSet은 Comparable 인터페이스를 구현하는 요소 클래스를 사용하거나, 생성자에 Comparator를 지정하여 요소들의 정렬 기준을 설정할 수 있습니다.
요소 클래스가 Comparable을 구현하지 않고, 별도의 정렬 기준을 지정하지 않으면 요소의 클래스는 **ClassCastException**을 발생시킬 수 있습니다.

  • TreeSet<E> = Set<E>의 기본기능 + 정렬/검색 기능 추가

         public class Ex1_TreeSetBasic {

                public static void main(String[] args) {

                    TreeSet<Integer> treeSet = new TreeSet<>();

                    for (int i = 50; i >0; i-=2) {  treeSet.add(i);      }
                    System.out.println(treeSet.toString());
                    //출력 : [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]


                    //#1.first() : 정렬된 순서에서 가장 작은 객체를 반환
                    System.out.println(treeSet.first()); //출력 : 2


                    //#2.last() : 정렬된 순서에사 가장 큰 (마지막 순서) 객체를 반환
                    System.out.println(treeSet.last()); //출력 : 50 


                    //#3.lower(E element) : 매개변수로 입력된 원소보다 작은 수들 중 가장 큰 수를 리턴
                    System.out.println(treeSet.lower(26)); //출력 : 24


                    //#4.higher() : 매개변수로 입력된 원소보다 큰 수들 중 가자 작은 수를 리턴
                    System.out.println(treeSet.higher(26)); //출력 : 28


                    //#5.floor(E element) : 입력된 매개변수의 원소와 비교하여 같거나 작은 수들 중 가장 큰 수를 리턴
                    System.out.println(treeSet.floor(25)); //출력 : 24
                    System.out.println(treeSet.floor(26)); //출력 : 26


                    //#6.ceiling(E element) : 입격된 매개변수의 원소와 비교하여 같거나 큰 수들 중 가장 작은 수를 리턴
                    System.out.println(treeSet.ceiling(25)); //출력 : 26
                    System.out.println(treeSet.ceiling(26)); //출력 : 26

 

TreeSet에서 요소들의 크기 비교는 Comparable 인터페이스 또는 Comparator를 통해 이루어집니다.

  1. Comparable 인터페이스를 구현한 요소 클래스를 사용하는 경우 - java.lang.Comparable<T> interface 구현 Comparable 인터페이스를 구현한 클래스의 객체는 compareTo() 메서드를 오버라이드하여 구현해야 합니다. 이 메서드는 두 개의 객체를 비교하여 순서를 결정합니다. TreeSet은 이 메서드를 사용하여 요소들을 정렬하고 크기를 비교합니다. 예를 들어, **treeSet.add(obj)**로 TreeSet에 객체를 추가하면 TreeSet은 **obj.compareTo()**를 호출하여 요소의 크기를 비교하고 적절한 위치에 삽입합니다.

  2. Comparator를 사용하는 경우- java.util.Comparator<T> interface 객체 제공 Comparator는 Comparable을 구현하지 않은 클래스의 요소들을 정렬하기 위해 사용됩니다. Comparator는 compare() 메서드를 구현해야 합니다. TreeSet의 생성자나 **treeSet.add(obj)**와 같은 메서드에 Comparator를 전달하여 TreeSet의 정렬 기준을 지정할 수 있습니다. Comparator를 사용하면 Comparable을 구현하지 않은 클래스의 요소들도 TreeSet에 저장할 수 있습니다.

크기 비교의 결과는 다음과 같습니다:

  • compareTo() 또는 compare() 메서드가 0보다 작은 값 (-1) 을 반환하면, 첫 번째 객체는 두 번째 객체보다 작은 것으로 간주됩니다.
  • compareTo() 또는 compare() 메서드가 0을 반환하면, 두 객체는 동일한 크기로 간주됩니다.
  • compareTo() 또는 compare() 메서드가 0보다 큰 값(1)을 반환하면, 첫 번째 객체는 두 번째 객체보다 큰 것으로 간주됩니다.
           
           class MyClass {
                int data1;
                int data2;

                public MyClass(int data1 ,  int data2) {
                    this.data1 = data1;
                    this.data2 = data2;
                }
            }
            
            //상속과 다형성에 의해서 부모:Comparable 자식 : MyComparableClass 
            class MyComparableClass implements Comparable<MyComparableClass> {
                //MyComparableClass라는 타입을 <>에 작성해 주었고, 이 클래스는 비교에 관련된 내용을 포함시키는데,
                //Comparable이라는 인터페이스를 implements로 구현해주어야 합니다. 
                int data1;
                int data2;

                public MyComparableClass  (int data1 ,  int data2) {
                    this.data1 = data1;
                    this.data2 = data2;
                }
                
                
                //Comparable 인터페이스 안에는 미완성 메서드가 있는데 이 메서드는 compareTo이며, 
                //이 메서드 안에서 대소 크기비교의 기준을 설정 할 수 있습니다. (음수, 0, 양수)
                @Override
                public int compareTo(MyComparableClass o) {
                    if (this.data1 < o.data1) return -1;
                    else if (this.data1 == data2) return 0;
                    else return 1;
                }
            }
728x90
반응형
728x90

컬렉션이란 배열처럼 동일한 타입을 묶어서 관리하는 자료구조입니다. 저장 공간의 크기 (capacity)를 동적으로 관리합니다.

프레임워크란 클래스를 정의하거나 설계할 때 어떤 원칙이나 구조를 가지고 클래스와 인터페이스를 설계한 것을 프레임워크라고합니다. ( 클래스와 인터페이스의 모임)

컬렉션 프레임워크란❓

컬렉션 프레임워크(Collection Framework)리스트, 스택, 큐, 트리 등의 자료구조에 정렬 , 탐색 등의 알고리즘을 구조화 해놓은 프레임워크이자 java에서 데이터 그룹을 다루기 위한 클래스들의 집합입니다.

프레임워크는 데이터 그룹을 저장하고 조작하기 위한 표준화된 방법을 제공하여 개발자들이 데이터 구조를 효율적으로 관리할 수 있도록 돕습니다. 이러한 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현됩니다.

컬렉션 프레임워크는 다양한 인터페이스와 클래스로 구성되어 있으며, 각각의 클래스는 특정한 데이터 구조와 동작 방식을 가지고 있습니다. 주요한 인터페이스와 클래스로는 다음과 같은 것들이 있습니다

컬렉션 프레임워크는 데이터 그룹을 다루기 위한 다양한 기능들을 제공합니다. 데이터의 추가, 삭제, 검색, 정렬 등을 쉽게 처리할 수 있으며, 각각의 클래스는 내부적으로 데이터 구조를 효율적으로 관리하도록 구현되어 있습니다. 이를 통해 개발자는 데이터 구조에 대한 세부적인 관리 없이 간편하게 데이터를 다룰 수 있습니다.

1. List<E>

순서가 있는 데이터 그룹을 저장하는 인터페이스입니다. 컬렉션은 제네릭타입으로 선언되고 동일한 타입을 저장할 때, 제네릭 타입으로 선언된 데이터들만 초기화가 가능합니다. 중복된 요소를 허용하며, 대표적인 클래스로는 ArrayList, LinkedList, Vector 등이 있습니다.

배열의 경우, 초기화되면 저장크기가 고정이 되어 이 후 각 인덱스에 저장된 값들을 제거하거나 추가하더라도 인덱스의 총 길이는 동적으로 바뀌지가 않습니다. LIST의 경우, 동적 컬렉션을 생성하여 데이터의 추가 삭제가 가능합니다. 그에 따른 저장공간의 길이도 자동으로 늘어나고 줄게 됩니다.

💡 List <E> 컬렉션의 객체 생성 방법

 

List <E> interface의 구현 클래스 생성자로 동적 컬렉션 생성 (데이터의 추가 삭제 가능)

  • 기본 생성자의 경우 원소 개를 저장할 수 있는 저장공간(capacity)를 확보합니다. 필요한 경우에 추가한다면 JVM이 동적으로 저장공간의 크기를 자동적으로 추가합니다. ex) List<Integer> aList1 = new ArrayList<Integer>(); → capacity(10)
  • 생성자 매개변수로 저장공간의 크기를 직접 넘겨줄 수 있습니다. (단, LinkedList<E>는 제외, capacity 지정 불가능) ex) List<Integer> aList2 = new ArrayList<Integer>(20) → capacity(20)

Arrays.asList( T(개수가 지정되지 않은 매개변수) ..) 메서드를 이용하여 정적 컬렉션 생성

  • 데이터의 추가(add) 삭제(remove) 불가능하며, 데이터의 변경(set)은 가능합니다. → 고정된 데이터를 저장하고 활용하고자 할 때 사용
    ex) List<제네릭타입지정> asList1 = Arrays.asList(제네릭타입데이터들);
    List<Integer> aList1 = Arrays.asList(1,2,3,4);
    내부적으로는 1,2,3,4가 담겨져 있는 Integer배열이 만들어졌고, 해당 배열을 List로 감싼 객체가 생성됩니다.
    여기서 내부적으로는 데이터가 고정적인 배열이 생성된 것이라 add() , remove()는 사용이 불가하고,
    데이터의 수는 변하지 않고 값만 변하게 하는 set() 메서드만 사용가능합니다.
    → 배열처럼 수집(collect)한 원소(Element)를 인덱스(index)로 관리 List<E>의 구현 클래스는 아래의 모든 메서드를 포함합니다.



https://velog.io/@mmy789/Java-컬렉션-프레임워크-1

 

  • toArray()메서드 : toArray() 메서드는 ArrayList의 요소들을 배열로 변환하는 메서드입니다. 이 메서드를 호출하면 ArrayList의 요소들이 배열로 복사되어 반환됩니다. toArray() 메서드에는 두 가지 형태가 있습니다
    1. 매개변수가 없는 toArray():
      • 이 형태의 toArray() 메서드는 Object 배열로 요소들을 복사하여 반환합니다.
      • 반환된 배열의 타입은 Object 배열이므로, 원하는 타입으로 형변환을 해주어야 합니다.
    2. 매개변수가 있는 toArray(T[] a):
      • 이 형태의 toArray() 메서드는 지정한 타입의 배열 **a**를 매개변수로 받아, 해당 배열로 요소들을 복사하여 반환합니다.
      • 만약 매개변수로 전달된 배열의 크기가 ArrayList의 크기보다 작을 경우, 새로운 배열이 생성되어 요소들이 복사됩니다.
      • 만약 매개변수로 전달된 배열의 크기가 ArrayList의 크기보다 크거나 같을 경우, ArrayList의 요소들이 매개변수로 전달된 배열에 복사되고, 남는 공간은 null로 채워집니다.
            Object[] object = aList3.toArray();
            System.out.println(Arrays.toString(object));
    
            Integer[] integer1 = aList3.toArray(new Integer[0]);
            System.out.println(Arrays.toString(integer1));  //출력 : [1,2,3]
            -> list인 aList3의 size()보다 작성된 선언공간이 크면 자동적으로 list의 size 크기를 가지는 배열이 생성
    
            Integer[] integer2 = aList3.toArray(new Integer[5]);
            System.out.println(Arrays.toString(integer2)); //출력 [1,2,3,null,null]
            -> list인 aList3의 size()가 작성된 선언공간 new Integer[5]보다 작은 경우 데이터가 채워진
               이 후의 공간에는 null값이 담기게 된다.


대표적인 List<E> 컬렉션의 클래스

  1. ArrayList<E> : ArrayList<E>는 제네릭을 사용하여 요소를 저장하는 동적 배열 클래스입니다. <E>는 제네릭 타입 매개변수로, ArrayList가 저장할 요소의 타입을 지정하는데 사용됩니다.
    ArrayList를 사용하려면 import java.util.ArrayList; 문을 통해 java.util 패키지에서 해당 클래스를 임포트한 후, 다음과 같이 ArrayList 객체를 생성하고 요소를 추가하고 접근할 수 있습니다

            import java.util.ArrayList;
    
            public class Main {
                public static void main(String[] args) {
                    ArrayList<String> list = new ArrayList<String>();
    
                    // 요소 추가
                    list.add("사과");
                    list.add("바나나");
                    list.add("딸기");
    
                    // 요소 접근
                    String fruit = list.get(1);
                    System.out.println(fruit); // 출력: 바나나
    
                    // 요소 삭제
                    list.remove(0);
                    System.out.println(list); // 출력: [바나나, 딸기]
                }
            }
    
            위의 예제에서 <String>은 ArrayList가 문자열 요소를 저장한다는 것을 명시하고 있습니다. 
            따라서 ArrayList에는 문자열만 저장할 수 있습니다. 만약 다른 타입의 요소를 저장하려면 
            <E> 자리에 해당 타입을 지정하면 됩니다.
  2. Vector<E> : Vector<E>는 Java의 컬렉션 프레임워크에서 제공하는 클래스로, 동적으로 크기가 조정되는 배열 형태의 자료구조입니다. ArrayList와 아주 유사하게 동일한 타입의 객체수집 (collection)` , 메모리의 동적할당, 데이터의 추가, 변경, 삭제 등의 메서드를 사용할 수 있는 공통적 기능을 제공하지만, 큰 차이점으론 Vector는 스레드 동기화(synchronized)가 되어 있어 멀티스레드 환경에서 안전하게 사용할 수 있습니다.

  3. LinkedList<E> : LinkedList<E>는 연결 리스트(linked list) 데이터 구조를 기반으로한 리스트(List) 구현체입니다. 앞뒤 요소(Element)로 정보의 데이터 위치를 관리합니다. LinkedList는 요소들을 연결한 노드(Node)로 구성되며, 각 노드는 데이터 요소와 다음 노드를 가리키는 참조(주소)로 이루어져 있습니다.

    각 데이터들의 왼쪽 오른쪽 데이터들의 위치를 알고 있습니다. 이러한 구조로 인해 요소들의 추가, 삭제, 삽입이 상대적으로 빠르게 이루어질 수 있지만, 검색(get)은 속도가 더 늦게 진행됩니다. (순서를 세어서 해당 토드를 찾아야 하기때문에)

  4. ArrayList<E>와 유사하지만 1) 디폴트 저장공간(10)만 사용하며 2) 생성자로 저장공간의 크기 지정이 불가한 점, 3) 데이터의 내부 저장방식이 index가 아닌 앞 뒤 객체의 위치정보를 저장한다는 차이점이 있습니다. 

        ArrayList<E>와 LinkedList<E> 가장 큰 차이점은 내부적인 데이터 구조입니다.

  1. 내부 데이터 구조:
    • ArrayList: 배열(Array)을 기반으로한 동적 배열 구조입니다. 요소들은 인덱스에 따라 순차적으로 저장됩니다. 인덱스로 직접적인 접근이 가능하며, 요소의 삽입과 삭제가 느리지만 요소의 조회가 빠릅니다.
    • LinkedList: 연결 리스트(Linked List)를 기반으로한 노드(Node)들이 연결된 구조입니다. 각 요소는 이전 노드와 다음 노드의 참조(주소)로 연결되어 있습니다. 요소의 삽입과 삭제가 빠르지만 인덱스로 직접적인 접근이 불가능하며, 요소의 조회가 느립니다.
  2. 데이터 접근 시간:
    • ArrayList: 인덱스로 요소에 직접 접근할 수 있으므로 요소의 조회가 빠릅니다. 삽입과 삭제가 배열의 크기 변동에 따라 발생하므로 비용이 크고, 요소를 중간에 삽입 또는 삭제하는 경우에는 뒤따라오는 요소들을 한 칸씩 이동해야 합니다.
    • LinkedList: 요소를 순차적으로 따라가야 하므로 인덱스로 직접 접근하는 것보다 접근 시간이 더 오래 걸립니다. 하지만 요소의 삽입과 삭제는 노드의 연결 관계만 변경하면 되므로 비교적 빠릅니다.
  3. 메모리 사용:
    • ArrayList: 내부적으로 배열을 사용하기 때문에 요소들을 연속적인 메모리 공간에 저장합니다. 따라서 공간 효율성이 높습니다.
    • LinkedList: 노드들이 연결되어 있기 때문에 각 요소마다 추가적인 노드 객체와 참조(주소)를 저장해야 하므로 메모리 사용량이 더 큽니다.

두 클래스는 사용 목적과 상황에 따라 선택되어야 합니다. ArrayList는 요소의 조회가 많고, 중간에 삽입/삭제가 적은 경우에 유리하며, LinkedList는 삽입/삭제가 빈번하고 순차적인 접근이 주로 발생하는 경우에 유리합니다.

 

 

728x90
반응형
728x90

 

✅ 제네릭 클래스 vs 제네릭 메서드의 차이점

  1. 범위: 제네릭 메서드는 특정 메서드 내에서만 제네릭 타입을 사용하고, 해당 메서드의 매개변수 타입이나 반환 타입을 제네릭으로 정의합니다. 반면에 제네릭 클래스는 클래스 전체에서 제네릭 타입을 사용하고, 클래스의 필드, 메서드, 생성자 등에서 제네릭 타입을 사용할 수 있습니다.

  2. 사용 방법: 제네릭 메서드는 메서드 선언 시에 타입 매개변수를 선언하고, 해당 메서드 내에서만 해당 타입을 사용합니다. 제네릭 클래스는 클래스 선언 시에 타입 매개변수를 선언하고, 클래스 전체에서 해당 타입을 사용할 수 있습니다.

  3. 타입 유연성: 제네릭 메서드는 각 메서드 호출 시에 사용하는 타입을 동적으로 지정할 수 있으므로, 호출할 때마다 다른 타입을 사용할 수 있습니다. 제네릭 클래스는 클래스 인스턴스를 생성할 때 타입을 지정하고, 해당 인스턴스의 타입은 고정됩니다.

  4. 코드 재사용성: 제네릭 메서드는 특정 메서드 내에서만 사용되므로 해당 메서드가 호출되는 범위 내에서만 재사용이 가능합니다. 제네릭 클래스는 여러 메서드에서 공통으로 사용되는 제네릭 타입을 정의하므로, 클래스 전체에서 재사용이 가능합니다.
    • 와일드 카드 : 불특정한 타입을 나타내는 데에 사용되며, 주로 제네릭 타입의 매개변수나 변수를 선언할 때 사용됩니다. 제네릭 클래스를 생성할 때, 참조변수에 지정된 제네릭 타입과 생성자에 지정돤 제네릭 타입은 일치해야 합니다. 만약 일치하지 않으면 컴파일 에러가 발생합니다.
      • 이때 제네릭 타입으로 ‘와일드 카드’를 사용하면 extends와 super로 상한과 하한을 제한할 수 있습니다.
      1. ? - 물음표 와일드카드(Unbounded Wildcard): 어떤 타입도 가능한 경우를 의미합니다. 예를 들어, **List<?>**는 어떤 종류의 요소든지 포함할 수 있는 리스트를 나타냅니다.
      2. ? extends 타입 - 상한 와일드카드(Upper Bounded Wildcard): 특정 타입 또는 그 타입을 상속받은 타입들만 가능한 경우를 의미합니다. . 예를 들어, List<? extends Number>는 Number 타입 또는 Number를 상속받은 타입들만인Integer, Double, Float 등을 받을 수 있는 리스트를 나타냅니다.
      3. ? super 타입 - 하한 와일드카드(Lower Bounded Wildcard) : 특정 타입의 상위 타입을 나타냅니다. 예를 들어, **List<? super Integer>**는 Integer 클래스와 그 상위 타입인 Number, Object 등을 받을 수 있는 리스트를 나타냅니다. 이렇게 사용되면 해당 타입과 그 상위 타입들에 대해서만 동작할 수 있습니다.

 

🎈 제네릭 타입 범위 제한

제네릭 타입 범위 제한(Generics Type Bounds)제네릭 클래스나 메서드에서 허용되는 타입의 범위를 제한하는 기능입니다. 이를 통해 제네릭 타입을 사용할 때 특정한 종류의 타입만을 허용하도록 제한할 수 있습니다. 이는 코드의 안정성과 타입 안정성을 높이는 데 도움이 됩니다.

  1. 타입 안정성 보장 : 특정한 종류의 타입만을 허용하므로, 잘못된 타입이 사용되는 것을 방지할 수 있습니다. 이로 인해 컴파일 시점에서 타입 체크를 수행하여 런타임 에러를 방지할 수 있습니다.

  2. 코드 재사용성 향상 : 특정한 종류의 타입으로 제한하면 해당 타입에 대한 특정한 동작 또는 기능을 구현할 수 있는 제네릭 클래스나 메서드를 작성할 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성이 향상됩니다.

 

🎈 제네릭 타입 범위 제한 종류

  1. 제네릭 클래스의 타입 제한
    • 상위 경계 (Upper Bound)를 사용하여 제한할 수 있습니다. 상위 경계 (Upper Bound) : extends 키워드를 사용하여 상위 클래스나 인터페이스를 제한합니다. 상위 경계를 설정하면 해당 클래스 또는 인터페이스와 그 자손 클래스들만이 타입 인자로 사용될 수 있습니다.
    • 타입 제한 기본 문법 구조 접근지정자 class 클래스명< T extends 상위클래스명> { / 타입 T를 사용한 코드 / } → 클래스 / 인터페이스 상관없이 항상 extends 사용 (인터페이스로 작성되었음에도 implements를 사용하지 않습니다.)
  2. 제네릭 메서드의 타입 제한
    • 제네릭 메서드의 타입 제한은 메서드에 전달되는 타입 인자의 범위를 제한하는 방법입니다.
      이를 통해 특정한 타입 또는 그 타입의 상위 또는 하위 타입만을 인자로 받을 수 있도록 제한할 수 있습니다.
    • 접근지정자<T extends 상위클래스명>T 메서드이름 (T t) {/ 부모 클래스의 메서드 사용 가능 /}
      (인터페이스로 작성되었음에도 implements를 사용하지 않습니다.)
  3. 일반 메서드인데 매개변수로서 제네릭을 사용할 때의 제네릭 클래스 타입제한
    • 메서드의 매개변수로 제네릭 클래스 객체가 오는 경우의 타입제한

 

✅ 제네릭 타입 범위 제한 예시 코드

 

        1. 특정 클래스를 상속받은 타입 제한
        T는 SomeClass 클래스 또는 그의 하위 클래스만을 인자로 받을 수 있습니다.
        public <T extends SomeClass> void methodName(T arg) {
            // 메서드 내용
        }

        2. 인터페이스를 구현한 타입 제한
        T는 SomeInterface 인터페이스를 구현한 타입만을 인자로 받을 수 있습니다.
        public <T implements SomeInterface> void methodName(T arg) {
            // 메서드 내용
        }

        3. 여러 개의 타입 제한을 조합
        T는 SomeClass 클래스를 상속받고 동시에 SomeInterface 인터페이스를 구현한 타입만을 인자로 받을 수 있습니다.
        public <T extends SomeClass & SomeInterface> void methodName(T arg) {
            // 메서드 내용
        }

 

하위 경계 (Lower Bound) : super 키워드를 사용하여 하위 클래스를 제한합니다. 하위 경계를 설정하면 해당 클래스와 그 상위 클래스들만이 타입 인자로 사용될 수 있습니다.

제한된 타입 파라미터 (Multiple Bounds) : 하나의 타입 파라미터에 대해 여러 개의 상위 경계를 지정할 수 있습니다. extends 키워드를 사용하여 여러 클래스나 인터페이스를 상위 경계로 지정할 수 있습니다.

 

🎈 제네릭(Genric)의 상속

  • 부모클래스가 제네릭인 경우 자식클래스 또한 제네릭 (부모의 제네릭 변수를 그대로 물려받음)
  • 하위 클래스에서 타입 매개변수에 대한 추가적인 제한이나 확장도 가능합니다. 예를 들어, 상위 클래스의 타입 매개변수를 상속받은 타입 매개변수로 제한하거나, 추가적인 타입 매개변수를 정의할 수도 있습니다.
728x90
반응형
728x90

 

제네릭(Generic)은 Java에서 도입된 기능으로 컬렉션 클래스나 메서드의 매개변수, 반환 타입 등을 일반화하는 방법을 제공하는 기능입니다. 제네릭은 클래스, 인터페이스, 메서드에서 사용할 수 있습니다. 클래스나 인터페이스에 제네릭을 적용하면 해당 클래스나 인터페이스의 필드, 메서드, 생성자에서 사용되는 타입을 일반화할 수 있습니다. 메서드에 제네릭을 적용하면 해당 메서드에서 사용되는 타입을 일반화할 수 있습니다.
제네릭은 자바에서 데이터 타입을 파라미터와(Parameterize) 할 수 있는 방법을 제공하는 프로그래밍 기법입니다.그 자체로 타입은 아니지만, 클래스나 인터페이스, 메서드를 정의할 때 사용하는 타입 매개변수를 가리킵니다.

 

https://www.youtube.com/@doit4312/playlists

 

제네릭은 Java 5부터 도입되었으며, 컴파일 시점에서 타입 체크가 이루어지기 때문에 타입 안정성을 보장하고 형변환을 줄일 수 있습니다. 또한, 제네릭을 사용하면 코드의 가독성도 높아지고 오류를 사전에 방지할 수 있습니다.
예로 들면, 제네릭을 사용하면 컬렉션에 저장되는 객체의 타입을 컴파일 시점에 체크할 수 있고, 타입 안정성을 보장받을 수 있습니다. 이를 통해 잘못된 타입의 객체를 저장하거나 꺼낼 때 발생하는 런타임 오류를 사전에 방지할 수 있습니다.

 

https://www.youtube.com/@doit4312/playlists

 

 

🎈 Generic 클래스 / 인터페이스 정의 문법 구조

  • 제네릭을 사용하기 위해서는 타입 매개변수를 선언해야 합니다. 타입 매개변수는 일반적으로 대문자로 표기하며, 클래스나 메서드에 선언됩니다. 제네릭 타입을 사용할 때에는 실제 타입으로 대체하여 사용하게 됩니다. 제네릭 클래스는 객체를 만들 때 타입이 정해집니다.

  • 일반적으로 <T>와 같이 꺽쇠(<>) 안에 대문자로 표현된 타입 매개변수를 사용합니다. 이후에 해당 타입 매개변수를 클래스의 필드, 메서드의 매개변수, 반환 타입 등에 사용하여 일반적인 코드를 작성할 수 있습니다. 실제 사용할 때는 타입 매개변수에 구체적인 타입을 지정하여 사용합니다. < > 꺽쇠 안에 작성되는 건 T, K , V 같은 변수이름을 작성하는데, 변수 이름이라 사용자가 마음대로 작성한 후 기입해도 되지만 일반적으로 영 대문자 하나를 사용합니다.


    ex) 제네릭 타입 변수명이 하나인 경우 
             접근지정자 class(또는 interface) 클래스명 <T> {
                       //타입 T를 사용한 코드
           }

     ▶   public class MyClass<T> { 
           //여기서의 T는 아직 정해지지 않았고, 이 후 어떤 타입을 작성하냐에 따라 달라집니다.
                  private T t;
                  public T get() {
                  return t;
              }
                  public void set (T t) {
                      this.t = t;
              }
      }



    ex) 제네릭 타입 변수명이 두개인 경우    
             접근지정자 class(또는 interface) 클래스명 <K, V> {
                       //타입 K, V를 사용한 코드
           }

     ▶   public interface MyInterface<K, V> { 
           //이 후, K와 V의 타입은 객체를 생성하면서 정해주려고 제네릭으로 선언해두었습니다.
                 public abstract void setKey( K k);
                 public abstract void setValue( V v);
                 public abstract void getKey( );
                 public abstract void setKey( );

           }

 

 

예를 들어, ArrayList 클래스는 제네릭을 사용하여 여러 종류의 객체를 저장할 수 있습니다. ArrayList<Integer>는 정수형 객체만을 저장할 수 있는 리스트를 의미하고, ArrayList<String>은 문자열 객체만을 저장할 수 있는 리스트를 의미합니다. 이렇게 제네릭을 사용하면 타입에 따라 다양한 종류의 리스트를 생성할 수 있습니다.

 

 

🎈 Generic 클래스 / 인터페이스 객체 생성 문법 구조

  • 객체 생성** 객체 생성 시 제네릭 타입을 지정하지 않으면 올 수 있는 Type 중 최상위 클래스(Object)로 인식 MyClass mc = new MyClass(); == MyClass<Object< mc = new MyClass<>(); 동일한 의미!
  • 클래스명<실제 제네릭 타입> 참조변수명 = new 클래스명<실제 제네릭 타입>(); or 클래스명 <실제 제네릭 타입 > 참조변수명 = new 클래스명<>(); → 생성자의 경우 내부의 타입 생략 가능

 

//T를 매개변수로 하는 제네릭 클래스 생성
class MyClass<T> {
	private T t;
	public T get() {
		return t;
	}
	public void set(T t) {
		this.t = t;
	}
}

public class Ex10_SingleGenericBasic {

	public static void main(String[] args) {

		MyClass	<String> mc1 = new MyClass<String>();
		mc1.set("안녕하세요");
		System.out.println(mc1.get());
		
		
		
		MyClass <Integer> mc2 = new MyClass<>(); //= 뒤의 Integer는 생략가능
		mc2.set(8);
		System.out.println("제 나이는 " + mc2.get() + "살입니다.");
		
		MyClass<Integer> mc3 = new MyClass<>();
		//mc3.set("안녕"); //강한 타입체크 Integer로 정해진 타입에 String 타입을 넣어서 바로 에러가 발생
	}

}

 

💡 제네릭의 기본 개념은 클래스 내에 사용되는 타입을 클래스가 정의될 때가 아닌 객체를 생성할 때 정의하겠다는 의미

 

🎈 Generic(제네릭) method(메서드)

 

제네릭 메서드는 일반 클래스안에 선언된 메서드 선언 시에 타입 매개변수를 사용하여 다양한 타입의 매개변수를 처리할 수 있도록 하는 기능입니다. 일반클래스 안에 만들어지기 때문에 클래스를 만드는 시점에서는 <T>를 정할 수 없고 메서드를 호출할 때, 제네릭 타입을 정할 수 있습니다. 단, 메서드의 매개변수 타입의 제네릭 변수가 사용된다면 반환 타입이나 매개변수의 타입에 일반적인 타입 대신 타입 매개변수를 사용하여 타입의 작성의 생략이 가능하는 등의 유연성을 제공합니다

  • 제네릭 메서드는 메서드 내에서 사용되는 타입 매개변수를 선언하여 메서드의 매개변수, 반환 값, 지역 변수 등에서 사용할 수 있도록 합니다.
  • 일반적으로 메서드의 시그니처에 <T>와 같은 형태로 타입 매개변수를 선언하며, 메서드 내에서 해당 타입을 사용할 수 있습니다.
  • 제네릭 메서드는 호출할 때 매개변수의 타입을 명시적으로 지정하거나, 컴파일러가 타입을 추론하여 암묵적으로 결정할 수 있습니다.

 

  • Generic 메서드 문법 예시
        public <T> void printArray(T[] array) {
            for (T element : array) {
                System.out.println(element);
            }
        }

        메서드의 타입 매개변수로 T를 사용하고 있습니다. 
        이제 T는 메서드 내부에서 사용할 수 있는 타입으로 간주됩니다.

        printArray 메서드는 배열을 받아서 배열의 모든 요소를 출력하는 역할을 합니다. 
        이때 배열의 타입은 제네릭 타입 T로 지정되어 있으므로, 어떤 타입의 배열이든지 처리할 수 있습니다. 
        예를 들어 Integer[], String[], Double[] 등을 인자로 전달할 수 있습니다.

        Integer[] intArray = { 1, 2, 3, 4, 5 };
        printArray(intArray); // Integer 배열을 출력

        String[] strArray = { "Hello", "World" };
        printArray(strArray); // String 배열을 출력
728x90
반응형

+ Recent posts