JNDI는 디렉터리 서비스에서 제공하는 데이터 및 객체를 발견하고 참고(look up) 하기 위한 자바 API .

자바애플리케이션을 외부 디렉터리 서비스에 연결( 데이터베이스 , LDAP 서버)

자바 애플릿이 호스팅 웹 컨테이너가 제공하는 구성 정보를 참고

JNDI (Java Naming and Directory Interface)는 Java 프로그래밍 언어로 작성된 애플리케이션에 이름 지정 및 디렉토리 기능을 제공합니다. 특정 이름 지정 또는 디렉터리 서비스 구현과 독립적으로 설계되었습니다. 따라서 새로운 서비스, 새로운 서비스, 이미 배포 된 서비스 등 다양한 서비스에 공통된 방식으로 액세스 할 수 있습니다.

JNDI 아키텍처는 API (Application Programming Interface)와 SPI (Service Provider Interface)로 구성됩니다. Java 애플리케이션은이 API를 사용하여 다양한 이름 지정 및 디렉토리 서비스에 액세스합니다. SPI는 다양한 이름 지정 및 디렉토리 서비스를 투명하게 플러그인 할 수 있도록하여 JNDI 기술의 API를 사용하는 Java 애플리케이션이 서비스에 액세스 할 수 있도록합니다.

Concepts :

컴퓨팅 시스템의 기본 기능은 이름 지정 서비스

이름을 개체와 연결하고 이름을 기반으로 개체를 찾는 수단

모든 컴퓨터ㅇ 프로그램이나 시스템을 사용할 때 개체 또는 다른 개체의 이름을 지정한다.

컴퓨터의 파일에 엑세스 하려면 해당 이름을 제공하야한다.

예를들어 인터넷 도메인 이름 시스템(DNS) 는 컴퓨터 이름 IP를 ip 주소에 매핑한다.

예시 : www.morriskim.com ==> 192.168.58.29

파일 시스템은 프로그램이 파일 내용에 엑세스 하는데 사용할 수 있는 파일 참조에 파일이름을 매핑한다.

두 가지에는 인터넷상의 개체 이름 지정부터 로컬 파일 시스템의 파일 이름지정에 이르기 까지 이름 지정서비스가 존재하는 광범위한 규모를 보여준다.

예시 : c:\bin\exec.bat

Naming system에서 개체를 조회하려면 개체 이름을 제공하게 되는데

이름 지정 시스템은 이름이 따라야하는 구문을 결정한다.

명명규칙이라고 하는데, 이름의 포현은 이름의 구성 요소를 표시하는 구성요소 구분으로 기호로 구성됨

명명 시스템 구성 요소 분리 기호 이름
unix "/" /usr/hello
DNS "." morris.com
LDAP " , " 또는 " =. ". Cn = Rosana Lee

Context

모든 컨텍스트에는 연관된 명명 규칙이 존제함

하위 컨텍스트에 바인딩 된 컨텍스트의 몇 가지 예입니다.

이처럼 Java 프로그래밍 언어를 사용해서 작성된 애플리케이션에 이름 지정 및 디렉토리 기능을 제공하는 API

Architecture

JNDI 아키텍쳐는 API DHK SPI (SERVICE PROVIDER INTERFACE) 로 구성된다.

JNDI 아키텍처

Java 어플리케이션은 JNDI API를 사용하여 이름 지정 및 디렉토리 서비스에 접근한다. SPI를 사용하면 다양한 이름 지정 및 디렉토리 서비스를 플러그인으로 할수 있으므로 JNDI API를 사용하는 Java 애플리케이션이 서비스에 엑세스 할 수 있다.

JNDI 는 Java SE 플랫폼에 포함되어 있습니다. JNDI를 사용하려면 JNDI 클래스와 하나 이상의 서비스 공급자가 있어야한다.

  • LDAP ( Lightweight Directory Access Protocol)
  • CORBA ( Common Object Request Broker Architecture), COS( Common Object Services) 이름 서비스
  • JAVA RMI
  • DNS (도메인 이름 서비스)

JNDI 5개 패키지로 나뉨

  • avax.naming
  • javax.naming.directory
  • javax.naming.ldap
  • javax.naming.event
  • javax.naming.spi

Tutorial

JNDI를 사용하여 네임 스페이스 업데이트를 위한 읽거 및 이름 지정 작업을 할 수 있다.

구성 하는 방법 :

이름 지정, 또는 디렉터리 서비스에 대한 작업을 수행하기 전에 네임 스페이스의 시작점인 초기 컨텍스트를 획득 해야합니다.

이름 지정 및 디렉터리 서비스에 대한 모든 방법이 일부 컨텍스트에 상대적으로 수행 되기 때문에 초기 컨텍스트를 얻으려면 아래의 단계를 거쳐야한다.

  1. 엑세스 하려는 서비스의 제공업체 선택
  2. 초기 컨텍스트에 필요한 구성 지정
  3. InitialContext 생성자 호출

1단계 서비스 공급자 선택

환경 들을 Hashtable을 만들고 여기에 서비스 공급자 클래스의 이름을 추가, 초기 컨텍스트에 사용할 서비스 공급자 지정하기

LDAP서비스를 사용하는 경우에는

Hashtable <String, Object> env = new Hashtable <String, Object> ();
env.put (Context.INITIAL_CONTEXT_FACTORY, 
        "com.sun.jndi.ldap.LdapCtxFactory");

파일 서비스를 지정하려면

Hashtable <String, Object> env = new Hashtable> String, Object> ();
env.put (Context.INITIAL_CONTEXT_FACTORY, 
        "com.sun.jndi.fscontext.RefFSContextFactory")

2단계 초키 컨텍스트의 정보 지정


package javax.naming.Context 를 보면 아래를 보면 Context에서는 미리 서비스 공급자를 설정하도록 설정이 이미 들어가있다.

아래의 getInitialContext 메서드를 보면 getInitialContext를 에 hashTable을

env.put (Context.PROVIDER_URL, "ldap : //ldap.wiz.com : 389");
env.put (Context.SECURITY_PRINCIPAL, "joeuser");
env.put (Context.SECURITY_CREDENTIALS, "joepassword");

아래를 보면 Java.naming Context 안에는 아래와 같은 메서드들이 이미 설정 되어있다.

3단계 초기 컨텍스트 생성

이전에 설정한 파일을 InitialContext를 생성자로 전달해서 생성

그러면 추후부터는 Context Object에 대한 참조가 있으므로 이름 지정 서비스에 접근이 가능하다.


사용방법

Naming Interface

JNDI 이름에 대한 구성요소 이름과 구문을 관리할수 있는 기능

 try {
            Name objectName = new CompositeName("java:comp/env/jdbc");
            Enumeration<String> elements = objectName.getAll();
            while(elements.hasMoreElements()){
                System.out.println(elements.nextElement());
            }
            objectName.add("AddComposition");
            objectName.get(1);

            System.out.println(objectName.get(objectName.size()-1));
        } catch (
            InvalidNameException e) {
            e.printStackTrace();
        }

/ 는 하위 컨텍스트의 구분 기호

출력 내용

java:comp
env
jdbc
AddComposition

Context Interface

컨텍스트에는 이름 지정 및 디렉터리 서비스에 대한 속성도 포함이 됨

Spring의 SimpleNamingContextBuilder NamingManager는 JNDI 공급자를 생성한 다음 NamingManager를 사용하여 빌더를 실행함

JNDI가 어떤것인지에 대해 알아보는 것만 알아보는 내용이었으므로 이후 세부적인 내용은 아래의 자세한 내용은 아래의 블로그를 참고하시면됩니다.

https://epthffh.tistory.com/entry/Spring%EC%97%90%EC%84%9C-JNDI%EC%84%A4%EC%A0%95hikaricp

참고 :

https://docs.oracle.com/javase/tutorial/jndi/index.html

https://docs.oracle.com/javase/tutorial/jndi/overview/index.html

https://www.baeldung.com/jndi

'ProgramLanguage > Java' 카테고리의 다른 글

JVM 메모리 구조 (1)  (0) 2020.03.22
Java8- Default 메서드(Abstract, Interface)  (0) 2020.03.18
JVM 튜닝  (0) 2020.02.10
Lambda_Expression(2)  (0) 2020.01.25
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26

JVM (Java Virtual Machine)

Java에서 프로그램을 실행 한다는 것은 컴파일 과정을 통해서 생성된 Class파일을 JVM으로 로딩하고 ByteCode를 해석(interpret) 하는 과정을 거쳐 메모리 등의 리소스를 할당하고 관리하며 정보를 처리하는 일련의 작업들을 포괄한다.

자바 가상머신

Thread 관리 및 Garbage Collection과 같은 메모리 정리 작업도 수행함.

  • 자바 애플리케이션을 클래스 로더를 통해 읽어 들여 자바 API와 함께 실행 하는것
  • JVM은 Java와 OS 사이에서 중개자 역할을 수행하여 JAVA가 OS에 구애 받지 않고 재사용을 가능하게 해줌
  • 메모리관리, Garbage collection을 수행
  • 스택 기반의 가상머신
  • "한번 작성해, 어디에서나 실행한다" 는 원칙!

JVM 구성 요소

JVM Architecture Diagram

ClassLoader를 통해 Class파일들을 로딩하고, 로딩된 Class파일들은 Execute Engine을 통해 해석 됨

해석된 프로그램은 Runtime Data Areas에 배치되어 실질적인 수행이 이루어짐

Java Source

사용자가 작성한 Java 코드 이다.

Java Compiler

Java Source를 JVM이 해석 할 수 있는 Java ByteCode로 변경

ClassLoader

JVM 내로 .class 파일들을 Load하여 Loading된 클래스들을 Runtime Data Area에 배치된다.

Execution Engine

Loading된 클래스의 ByteCode를 해석(interpret) 한다.

Runtime Data Area에 할당된 바이트 코드들은 ExecutionEngine에 의해 실행이 된다. 실행 엔진은 바이트 코드를 읽고 실행한다.

  • InterPreter : 바이트 코드명령어를 하나씩 읽어서 해석하고 실행.
    하나하나 해석은 빠르지만 전체적인 실행속도는 느리다는 단점.
  • JIT(Just In Time) Compiler : 인터프리터의 단점을 보완하기 위해 도입된것이 JIT.
    역할 : 인터프리터 방식으로 실행 되다가 같은 코드를 매번 해석하지 않고 실행 할때 컴파일을 하면서 해당코드를 NativeCode로 변경후에 캐싱한다. 이후에는 바뀐 부분만 컴파일하고 나머지는 캐싱된 코드를 사용함으로써 인터프리터의 속도를 개선할 수 있다.
  • Garbage Collector : 참조 되지 않는 오브젝트를 수집하고 제거

Runtime Data Area

JVM 이라는 프로세스가 프로그램을 수행하기 위해 OS에서 할당 받은 메모리 공간

Method Area

클래스, 변수, Method, static 변수, 상수 정보 등이 저장되는 영역이다.( 모든 Thread가 공유한다)

Heap Area

New 명령어로 생성된 인스턴스와 객체가 저장되는 구역 (GarbageCollection 이슈는 이 영역에서 일어나며, 모든 Thread가 공유)

Stack Area

Method 내에서 사용되는 값들( 매개변수, 지역변수, 리턴값) 등이 저장되는 구역으로 메소드가 호출될때 LIFO(last in first out : 마지막에 나온것이 먼저 나간다) 로 하나씩 생성되고 메소드 실행이 완료되면 LIFO로 하나씩 지워진다.

(각 Thread 별로 하나씩 생성됨)

PC Register

CPU의 Register와 역할이 비슷하다. 현재 수행중인 JVM 명령의 주소값이 저장된다( 각 Thread별로 하나씩 생성)

Native Method Stack

다른 언어(C/C++) 의 메소드 호출을 위해 할당되는 구역으로 언어에 맞게 Stack이 형성되는 구역

참고

https://d2.naver.com/helloworld/1230

JVM Performance Optimizing 및 성능 분석 사례 -1장 JVM 메모리 구조

'ProgramLanguage > Java' 카테고리의 다른 글

JNDI (Java Naming and Directory Interface)  (0) 2020.08.30
Java8- Default 메서드(Abstract, Interface)  (0) 2020.03.18
JVM 튜닝  (0) 2020.02.10
Lambda_Expression(2)  (0) 2020.01.25
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26

Java8에 interface에서 default 메서드가 추가 된다면 어떻게 될까?

public abstract class AbstractTest {
    abstract void printString();
}

public interface InterFaceTest {
    default void printString(){
        System.out.println("PrintInterface");
    }
}

이럴 경우 AbstractTest를 상속받고 InterFaceTest를 구현하면 어떻게 될까?
public class Test extends AbstractTest implements InterFaceTest {

}

이럴 경우에는 IDE에서는 아래와 같은 충돌로 에러를 검출한다.
Class 'Test' must either be declared abstract or implement abstract method 'printString()' in 'AbstractSimple'
이럴 경우에는 아래와 같이 AbstractClass의 메서드를 구현한다.
public class Test extends AbstractTest implements InterFaceTest {
    @Override
    public void printString() {

    }
}

정리
- default Method 와 조상 클래스의 메서드 간의 충돌 : 인터페이스에서 디폴트 메서드는 무시된다.
- 여러 인터페이스의 디폴트 메서드간의 충돌 : 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야한다.

'ProgramLanguage > Java' 카테고리의 다른 글

JNDI (Java Naming and Directory Interface)  (0) 2020.08.30
JVM 메모리 구조 (1)  (0) 2020.03.22
JVM 튜닝  (0) 2020.02.10
Lambda_Expression(2)  (0) 2020.01.25
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26

가비지 컬렉션 과정 - Generational Garbage Collection

GC에 대해서 알아보기 전에 알아야 할 용어가 있다. 바로 'stop-the-world'이다. stop-the-world란, GC을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것이다. stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 나머지 쓰레드는 모두 작업을 멈춘다. GC 작업을 완료한 이후에야 중단했던 작업을 다시 시작한다. 어떤 GC 알고리즘을 사용하더라도 stop-the-world는 발생한다. 대개의 경우 GC 튜닝이란 이 stop-the-world 시간을 줄이는 것이다.

- Young 영역(Yong Generation 영역): 새롭게 생성한 객체의 대부분이 여기에 위치한다. 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다. 이 영역에서 객체가 사라질때 Minor GC가 발생한다고 말한다.

- Old 영역(Old Generation 영역): 접근 불가능 상태로 되지 않아 Young 영역에서 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다. 이 영역에서 객체가 사라질 때 Major GC(혹은 Full GC)가 발생한다고 말한다.

참고 : [D2 Java Garbage Collection](https://d2.naver.com/helloworld/1329)

GC의 튜닝의 목적 두가지

* Old 영역으로 넘어가는 객체의 수를 최소화 하는것

* FullGC의 실행 시간을 줄이는것

Old 영역으로 넘어가는 객체의수 최소화 하기

JDK 7부터 본격적으로 사용할 수 있는 G1 GC를 제외한, Oracle JVM에서 제공하는 모든 GC는 Generational GC이다. 즉, Eden 영역에서 객체가 처음 만들어지고, Survivor 영역을 오가다가, 끝까지 남아 있는 객체는 Old 영역으로 이동한다. 간혹 Eden 영역에서 만들어지다가 크기가 커져서 Old 영역으로 바로 넘어가는 객체도 있긴 하다. Old 영역의 GC는 New 영역의 GC에 비하여 상대적으로 시간이 오래 소요되기 때문에 Old 영역으로 이동하는 객체의 수를 줄이면 Full GC가 발생하는 빈도를 많이 줄일 수 있다. Old 영역으로 넘어가는 객체의 수를 줄인다는 말을 잘못 이해하면 객체를 마음대로 New 영역에만 남길 수 있다고 생각할 수 있지만, 그렇게는 할 수는 없다. 하지만 New 영역의 크기를 잘 조절함으로써 큰 효과를 볼 수는 있다.

Full GC 시간 줄이기

Full GC의 실행 시간은 상대적으로 Minor GC(yong GCs)에 비하여 길다. 그래서 Full GC 실행에 시간이 오래 소요되면(1초 이상) 연계된 여러 부분에서 타임아웃이 발생할 수 있다. 그렇다고 Full GC 실행 시간을 줄이기 위해서 Old 영역의 크기를 줄이면 자칫 OutOfMemoryError가 발생하거나 Full GC 횟수가 늘어난다. 반대로 Old 영역의 크기를 늘리면 Full GC 횟수는 줄어들지만 실행 시간이 늘어난다. Old 영역의 크기를 적절하게 '잘' 설정해야 한다.

이후 자세한 튜닝 방법은 안알아도됨

참고 : [Naver D2](https://d2.naver.com/helloworld/37111)

'ProgramLanguage > Java' 카테고리의 다른 글

JVM 메모리 구조 (1)  (0) 2020.03.22
Java8- Default 메서드(Abstract, Interface)  (0) 2020.03.18
Lambda_Expression(2)  (0) 2020.01.25
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26
Java 와 객체 지향  (0) 2019.12.03

모던 자바( 자바8) 못다한 이야기를 보고 정리한 것입니다.

Functional Interface로 java.util.function 패키지 안에 포함되어있다.

람다식의 타입과 형변환

인터페이스인데 abstract 메소드가 하나만 존재하는 경우

Functional interface 4종류

  1. Function 타입
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

apply 메소드 하나만 가지고 있음

입력값과 리턴값이 있어야한다.

functional interface 는 anonymous 클래스 필요없이 사용이 가능하다.

function type : function패키지 안에 들어있다.

리턴타입 하나의 타입이 다른 타입으로 리턴 된것.

같은 타입을 리턴하는 것을 identity라고 한다.

같은 타입 T -> R은 일반적인 함수

T- >T 는 identity 함수가 된다 . 그 타입을 받아서 그타입으로 바로 넘기게 되는것

//Parameter : T , Return Type : R
//Function <R,T> 하나의 메소드를 호출
Function<String, Integer> toInt = new Function<String, Integer>() {
  @Override
  public Integer apply(String value) {
    return Integer.parseInt(value);
  }
};

//lambda로 표현하면 ?
Function<String, Integer> toIntLamda = (value)->Integer.parseInt(value);


Integer number = toInt.apply("200");
System.out.println(number);
  1. Consumer
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

Accept 메소드 하나만 있음.

void 로 리턴값을 가지고 있음 값은 있고 리턴은 없다.

Ex) 리턴값이 없을경우, 출력

//Basic
//Consumer<T>
//Parameter  : T , Return : void
Consumer<String> consumer = new Consumer<String>() {
  @Override
  public void accept(String s) {
    System.out.println(s);
  }
};

//lambda
Consumer<String> consumer2 = (value)->System.out.println(value);

consumer.accept("20");
consumer2.accept("20");
  1. Predicate
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

결과 값은 항상 boolean 값

function 인데 파라미터 값은 T이고 리턴 타입은항상 boolean 으로 출력

//Basic
Predicate<Integer> isPositive = new Predicate<Integer>() {
  @Override
  public boolean test(Integer integer) {
    return integer >0;
  }
};

System.out.println("is Positive ? "+isPositive.test(-1));
System.out.println("is Positive ? "+isPositive.test(2));

//응용
List<Integer> numbers = Arrays.asList(-5,-4,-3,-2,-1,0,1,2,3,4,5);

//조건
Predicate<Integer> isPositive2 = value -> value > 0;

List<Integer> positiveNumbers = new ArrayList<>();
for(Integer num : numbers){
  if(isPositive2.test(num)){
    positiveNumbers.add(num);
  }
}

아래와 같이 자주쓰이는 filter 메서드를 만들어서 condition만 변경 해주는 방식으로도 사용이 가능하다.

public static <T> List<T> filter(List<T> list, Predicate<T> condition){
  List<T> result = new ArrayList<>();
  for(T element: list){
    if(condition.test(element)){
      result.add(element);
    }
  }
  return result;
}
  1. Supplier

인자 값은 없고 return type만 존재한다.

일종의 lazy Evaliation 할 수 있다.

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

expensiveValue를 구하는 메서드를 일반 코드로 나타내면 아래와 같은 코드가 된다. 총 소요시간은 총 9초 이다.

 long start = System.currentTimeMillis();
        printIfValidIndex( 0, getVeryExpensiveValue());
        printIfValidIndex( -1, getVeryExpensiveValue());
        printIfValidIndex( -2, getVeryExpensiveValue());

        System.out.println("Take Time : "+((System.currentTimeMillis()-start)/1000));

메서드 :

    private static String getVeryExpensiveValue(){
        try{
            TimeUnit.SECONDS.sleep(3);
                        System.out.println("throw this area");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return "Morris";
    }

    private static void printIfValidIndex( int number, String valueSupplier){
        if(number >=0){
            System.out.println("The value is "+ valueSupplier + ".");
        }else {
            System.out.println("InValid");
        }
    }

결과 값

getVeryExpensiveValue 메서드가 항상 실행 된다.

그래서 비싼 비용의 메서드가 계속 실행된다는 단점이 있다.

실행 순서는 getVeryExpensiveValue () -> printIfValidIndex -> condition

throw this area
The value is Morris.
throw this area
InValid
throw this area
InValid
Take Time : 9

lazy 하게 실행 할수 있는 supplier로 수정을 해보자

long start = System.currentTimeMillis();
        printIfValidIndex( 0, () -> getVeryExpensiveValue());
        printIfValidIndex( -1, () -> getVeryExpensiveValue());
        printIfValidIndex( -2, () -> getVeryExpensiveValue());

        System.out.println("Take Time : "+((System.currentTimeMillis()-start)/1000));
    }

메서드

일반 코드에서 printIfValidIndex의 매개변수를 string에서 Supplier으로 변경을 하였다.

    private static String getVeryExpensiveValue(){
        try{
            TimeUnit.SECONDS.sleep(3);
              System.out.println("throw this area");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return "Morris";
    }

    private static void printIfValidIndex( int number, Supplier<String> valueSupplier){
        if(number >=0){
            System.out.println("The value is "+ valueSupplier.get() + ".");
        }else {
            System.out.println("InValid");
        }
    }

결과 값

throw this area
The value is Morris.
InValid
InValid
Take Time : 3

결과 값을 보면 3초 밖에 나오지 않는다 .

printIfValidIndex -> condition -> get을 하면 불러온다.

이렇게 알수 있는 것은 supplier를 사용하면 매개변수.get을 사용하는 시점에서 해당 메서드가 실행 되는 것을 알수 있다. 이렇게 하면 컴퓨팅 성능과 리소스를 줄일수 있을 것 같다.

'ProgramLanguage > Java' 카테고리의 다른 글

Java8- Default 메서드(Abstract, Interface)  (0) 2020.03.18
JVM 튜닝  (0) 2020.02.10
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26
Java 와 객체 지향  (0) 2019.12.03
Java jar파일 만들기  (0) 2019.08.04

드디어 solid에 대한 개념을 정리 하게 되었다.

객체 지향의설계의 5원칙 집대성

로버트 C. 마틴이 2000년대 초반 객체 지향 프로그래밍 및 설계의 다섯가지 기본원칙으로 제시함 .

응집도(high Cohesion)는 높이고, 결합도 (Loose Coupling)는 낮추라는 고전 원칙을 객체 지향의 관점에서 재정립한 것.

결합도와 응집도
결합도는 모듈간의 상호 의존 정도로서 결합도가 낮으면 모듈간의 상호 의존성이 줄어들어 객체의 재사용이나 수정, 유지 보수가 용이하다.
응집도는 하나의 모듈내부에 존재하는 구성요소들으 ㅣ기능적 관련성으로 응집도가 높은 모듈은 하나의 책임에 집중하고 독립성이 높아져 재사용이나 기능의 수정, 유지보수가 용이하다.

객체지향 SW 설계의 원칙 ① 개방-폐쇄 원칙

객체지향 SW 설계의 원칙 ② 사례연구, 단일 책임 원칙

객체지향 SW 설계의 원칙 ③ 인터페이스 분리의 원칙

객체지향 SW 설계의 원칙 ④ 리스코프 치환 원칙

SRP (Single Responsibility Principle) 단일 책임 원칙

어떤 클래스를 변경해야하는 이유는 오직 하나 뿐이어야 한다. - 로버트 C 마틴

책에서는 여자친구, 어머니, 직장상사, 소대장 등 다양한 사람들이 의존하는 남자친구로 설명을한다.

여러 사람들이 의존하는 남자친구는 피곤할수 밖에 없다.

여자친구- 남자친구, 어머니 - 아들, 직장상사 - 사원, 소대장 - 소대원

이렇게 역할을 분리하라는 것이 단일 책임 원칙이다.

장점 :

  • 이전에는 남자친구라는 이름으로 의존되고있는 것을 관리 하였는데, 역할을 분할하니 클래스 이름도 이해하기가 좋다.
  • 여자친구와 이별을 하더라도 어머니-아들, 직장상사- 사원, 소대장 - 소대원 에게는 영향을 받지 않는다.
  • 단일 책임 원칙은 속성, 메서드, 패키지, 모듈, 컴포넌트, 프레임워크 등에도 적요할 수 있는 개념이다.

잘못된 예

예시1) 남자와 여자를 사람으로 나타내는 클래스가 있다. 남자는 군대를 가야해서 사람이라는 클래스에 군번이라는 필드를 넣게 된다면? 여자는 군대를 가지 않지만 군번이라는 필드 에 값은 할당되지 않아도 계속 가지고 있어야하는 찜찜함이 있다.

이럴때는 남자, 여자가 공통적인 부분들을 사람이라는 클래스에 두고 남자, 여자 클래스에서 상속을 받아, 차이점을 구현하면 해결할수 있다.

OCP(Open Closed Principle) 개방 폐쇄 원칙

소프트 웨어 인티티(클래스, 모듈, 함수) 등 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야한다 - 로버트 c 마틴

자신의 확장에는 열려있고, 주변의 변화에 대해서는 닫혀 있어야한다

예제

한 운전자가 수동의 마티즈를 운전할때 창문 수동 개방, 기어 수동 조작에 대한 메서드를 사용 했는데, 나중에 쏘나타로 차를 변경하게 되면서 창문 수동 개방, 기어 자동 조작 에 대한 메서드를 사용하게 된다면 ?

운전자가 운전에 영향을 받아야하는가?를 물어보면서 상위 클래스 또는 인터페이스를 중간에 둠으로써, 다양한 자동차가 생긴다고 해도 객체 지향 세계의 운전습관에는 영향을 받지 않게 된다.

자동차가 생긴다고 하는것은 자동차 입장에서는 자신의 확장에 개방이 되어있고, 운전자입장에서는 주변의 변화에 폐쇄 되어있는 것이다.

또 다른 예를 들면, JDBC를 사용하는 클라이언트는 데이터베이스가 오라클에서 MySQL 로 바뀌더라도 Connection을 설정 하는 부분외에는 따로 수정하는 부분이 없다. 데이터베이스는 교체가 가능하므로 확장에 개방이 되어있고, 자바 애플리케이션은 데이터베이스라고 하는 주변의 변화에 닫혀 있다는 말이다.

LSP (Liskov Substitution Principle) 리스코프 치환 원칙

서브 타입은 언제나 자신의 기반 타입(base type) 으로 교체 할 수 있어야한다. - 로버트 c 마틴

상속은 조직도나 계층도가 아닌 분류도가 되어야한다!

  • 하위 클래스 is a kind of 상위 클래스 - 하위 분류는 상위 분류의 한 종류이다.

  • 구현 클래스 is able to 인터페이스 - 구현분류는 인터페이스 할 수 있어야 한다.

    인터페이스 할수 있어야한다라는 표현은 각 인터페이스 명에 따라 쉽게 이해할 수 있다.
    AutoCloseable - 자동으로 닫힐 수 있어야한다.
    Appendable - 덧붙일 수 있어야한다.
    Cloneable - 복제할 수 있어야한다.
    Runnable - 실행 할 수 있어야한다.

상위 클래스의 객체 참조 변수에는 하위 클래스의 인스턴스를 할당할 수 있다.

부모 자식관계의 잘못된 예를 보면

아버지 춘향이 = new 딸();

춘향이는 아버지형의 객체 참조변수 이기에 아버지객체가 가진 행위를 할 수 있어야하는데, 딸인 춘향이는 아버지의 역할을 할수가 없다.

아래와 같은 경우에는 동물 - 펭귄 구조(분류도)는 리스코프 치환 원칙을 만족 한다라고 할 수 있다.

동물 뽀로로 = new 펭귄();

정리하면 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없어야한다.

계층도/조직도 경우가 아니라, 분류도인 경우 하위에 존재하는 것들은 상위에 있는 것들의 역할을 하는데 전혀문제가 없다.

ISP (Interface Segregation Principle) 인터페이스 분리 원칙

클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다. - 로버트 C. 마틴

이전의 SRP를 설명할때 남자친구는 여자친구, 어머니, 직장상사, 소대장이 의존하고있는 남자친구에 대해 역할을 분리하는것이 좋다고 이야기를 했는데, 다른 방법으로는 ISP를 사용하여 나타낼 수 있다.

여자친구, 어머니, 직장상사, 소대장이 의존하고있는 남자친구의 역할의 메서드를 포함하는 인터페이스를 생성하고, 남자친구를 implements 해서 각 역할에 맞는 클래스를 생성하는것이 인터페이스 분할 원칙의 핵심이다.

인터페이스에 메서드를 정의하고,정의된 메서드들을 각 개별의 객체의 클래스에스 구현하는 것 이 핵심이라고 생각함

결론적으로 SRP, ISP는 같은 문제에 대한 두가지 다른 해결책이라고 볼수 있다.

프로젝트의 요구사항과 설계자의 취향에 따라 단일 책임 원칙이나 인터페이스분할 원칙중 하나를 선택해서 설계할 수 있다.

상위 클래스는 풍성할 수록 좋고, 인터페이스는 작을 수록 좋다.

DIP (Dependency Inversion Principle) 의존 역전 원칙

"고차원 모듈은 저차원 모듈에 의존하면 안된다. 이 두 모듈 모두 다른 추상화 된 것에 의존해야 한다.","추상화된 것은 구체적인 것에 의존하면 안된다. 구체적인 것이 추상화된 것에 의존해야한다."," 자주 변경되는 구체(Concrete) 클래스에 의존하지 마라" - 로버트 마틴

자동차 타이어에는 여러 종류가 있다. 스노우 타이어, 일반 타이어, 광폭타이어

자동차 타이어가 교체 될때마다 타이어클래스들을 변경 해야하지만, 상위에 인터페이스로 타이어라는 추상 객체를 만들어 상속하여 사용한다면, 타이어가 변경되도

자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어번 하기쉬운 것의 변화에 영향 받지 않게 하는것이 의존 역전의 원칙

상위 클래스일 수록 인터페이스일수록, 추상클래스일수록 변하지 않는 가능성이 높기에 하위 클래스나 구체 클래스가 아닌 상위 클래스, 인터페이스, 추상클래스를 통해 의존하라는것이 바로 의존 역전의 원칙

'ProgramLanguage > Java' 카테고리의 다른 글

JVM 튜닝  (0) 2020.02.10
Lambda_Expression(2)  (0) 2020.01.25
Java 와 객체 지향  (0) 2019.12.03
Java jar파일 만들기  (0) 2019.08.04
인터페이스와 추상클래스  (0) 2019.06.10

객체지향 책을 보고 정리 하는중입니다.

객체 지향은 인간 지향이다.

기계 종속적인 개발이 아니라 우리가 눈으로 보고 느끼고, 생활하는 현실 세계처럼 프로그래밍 할 수는 없을까? 라는 고민 속에서 객체 지향의 개념이 탄생했다.

함수로 인해 프로그래밍이 조금 편해지고 더욱 파격적인 제안이 나옴, 그게 바로 객체 지향이다.

객체 지향은 직관적이고 쉽고, 인간적이다는 증거를 이제부터 하나씩 알아보자

객체 지향의 4대 특성 - 캡! 상추다

캡 - Encapsulation, : 정보 은닉

상 - 상속(Inherit) : 재사용

추 - 추상화 (Abstraction): 모델링

다 - 다형성 (Polymorphism): 사용 편의

객체 지향에 대한 질문

  • 사람의 나이는 몇 살인가?

  • 김연아의 나이는 몇 살 인가?

  • 뽀로로의 나이는 몇 살인가?

  • 펭귄의 나이는 몇 살인가?

클레스: 객체 = 사람: 김연아 = 펭귄 : 뽀로로

추상화

추상 : 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용

공통 특성 / 공통 속성 추출

객체 지향의 추상화는 곧 모델링이다 라는 관점을 살펴보자

객체 지향의 4대 특성들은 클래스를 통해 구현된다.

객체는 세상에 존재하는 유일무이한 사물로 해석을 하고, 클래스는 분류, 집합. 같은 속성과 기능을 가진 객체를 총칭하는 개념 이라고 하면

사람 morris = new 사람();class Person {
String name;
int age;
int numberOfLeg ;
public void eat(){
System.out.println("냠냠");
}
}

public class ObjectMain{

public static void main( String []args){
Person morris = new Person();
morris.name = "Kim Morris";
morris.age = 29;
morris.numberOfLeg = 2;
}
}

참고할점은 사람의 다리는 전부 2개 이기 때문에, heap에 메모리를 줄여주기 위해서 static으로 변경하여 Static 영역에 배치 하게 하면 더 좋은 효과를얻을수 있다.

새로운 인스턴스(객체)를 생성하게 되면 또다른 참조변수를 만들고 heap에 Person 클래스를 다시 힙에 배치

자바에서는 포인터가 객체 참조변수에 할당된다는 사실 !!

Heap : Person의 인스턴스를 하나 만들어 heap에 배치 한다.

Stack : Person 객체에 대한 참조 변수 morris를 만든다.

Static : Person class의 String name, int age, int numberOfLeg, eat() 클래스가 전부 스태틱 영역에 배치 됨

아래와 같이 Person 클래스가 있을 경우, 객체를 생성하게 되면 메모리의 구조는 아래와 같다.

메모리는 ??

자바는 객체 지향의 추상화를 어떻게 지원을 할까? 바로 class 키워드를 통해 지원한다.

  • 상속을 통한 추상화, 구체화

  • 인터페이스를 통한 추상화

  • 다형성을 통한 추상화

추상화의 개념을 넓게 본다면 아래 내용도 포함된다.

  • OOP의 추상화는 모델링이다.

  • 클래스 : 객체 = 펭귄 : 펭수

  • 클래스 설계에서 추상화가 사용된다.

  • 클래스 설계를 위해서는 애플리케이션의 경계부터 정해야한다.

  • 객체지향에서 추상화의 결과는 클래스이다.

추상화에 대한 핵심을 다시 정리 해보면

추상화란 구체적인 것을 분해해서 관심 영역에 있는 특성만 가지고 재조합하는 것 => 모델링

추상화란 구체적인 것을 분해해서 관심 영역에 대한 특성만 가지고 재조합하는 것

추상화의 일반적인 뜻을 다시 살펴 보면

병원에 대한 애플리케이션이면 병원에는 환자라는 구체적인 사람으로 바꿀 수 있고, 설계도 달라질 것이다.

이럴때 내가 만들고자하는 애플리케이션은 어디에서 사용될 것인가? 를 질문을 하면된다.

여기서 애플리케이션 경계라는 것이 나오는데, 애플리케이션 경계를 Context라고 부르기도한다.

사람에 대한 클레스에 대해 공통적인 특성들을 나열하다보면 끝이 없다.

'ProgramLanguage > Java' 카테고리의 다른 글

Lambda_Expression(2)  (0) 2020.01.25
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26
Java jar파일 만들기  (0) 2019.08.04
인터페이스와 추상클래스  (0) 2019.06.10
람다 식 Lambda Expression (1)  (0) 2019.06.09

 

요즘은 다 ide로 제공하기 때문에 실제로 어떻게 흘러가는지 알수가 없다 . 예전에는 editplus로 코딩을 했었는데 …

패스트캠퍼스 java all in one 강의를 보면서 다시 한번 다듬는 생각에 다시 정리를 해야겠다는 생각이 들었다.

하나의 main클레스를 사용하여 테스트를 해보자

Java class file

package kr.co.morris;

class Main {
    public static void main(String []args){
        System.out.println("Hello Spring!");
    }
}

위의 파일을 실행 하려면

main 클레스가 있는 위치에서 우선 javac를 통하여 컴파일을 해야한다

$javac Main.java

컴파일을 하면 두개의 파일이 생성될것이다.

$ls
Main.java Main.class

여기서 실행하기 하려면 뒤의 확장자를 제외하고 java 명령어를 통하여 실행하면된다.

$java Main
Hello Spring!

Jar로 실행하기

-c 는 새로운 아카이브 형태로 생성한다는 옵션

-f 는 새로운 아카이브의 파일의 이름을 설명하는것

$ javac kr/co/morris/Main.java

$jar -cf morris.jar ./kr/*

$ls
ko
morris.jar

jar 실행시 -cp 클래스패스를 지정하고 실행하면된다.

$java -cp morris.jar kr.co.morris.Main

jar파일로 만 실행 하려면

$java -jar morris.jar
morris.jar에 기본 Manifest 속성이 없습니다.

Manifest 속성이 필요하다 . Manifest를 생성하자!!

해당 루트폴더에 manifest.txt 파일을 생성해주기

manifest.txt

Main-Class: kr.co.morris.Main

Jar 명령어를 사용하여 옵션에 m 옵션을 붙여주고 manifest.txt 파일을 추가 해주면 끝!

$jar -cmf manifest.txt morris02.jar ./kr/*

$java -jar morris02.jar 
Hello Spring!

'ProgramLanguage > Java' 카테고리의 다른 글

객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26
Java 와 객체 지향  (0) 2019.12.03
인터페이스와 추상클래스  (0) 2019.06.10
람다 식 Lambda Expression (1)  (0) 2019.06.09
익명클래스(Anonymous Class)  (0) 2019.06.08

인터페이스와 추상 클래스

인터페이스와 추상클래스의 차이

추상클래스

추상클래스

: 완성되지 못한 남겨진 설계도 라고 말을 한다.

클래스가 미완성이라는 것은 멤버의 개수에 관계된것이 아니라, 단지 미완성 메서드를 포함하고 있다는 의미한다. 어떤의미일까 ???

추상클래스로 인스턴스는 생성할 수 없다. 추상클래스는 상속을 통해 자손클래스에 의해서만 완성 할 수 있다.

조상 클래스로서의 의미 새로운 클래스를 작성하는데있어서 완전하지는 않아도 틀을 갖춘 상태에서 시작하는 것을 의미

추상클래스

abstract class ClassName{
  ...
}

추상 메서드

선언부만 작성하고 구현부는 작성하지 않은채로 남겨 둔것

그러기 때문에 미완성 메서드라고 말할 수 있다.

  • 선언부( O )
  • 구현부( X )

상속받는 클래스에 따라 달라질 수 있기 때문에 조상클래스에서는 선언부만 작성

abstract returnType methodName();

메서드의 이름과 메서드의 작업에 필요한 매개변수, 메서드의 반환값을 결정하는 것은 쉽지 않기 때문에 선언부만 작성해도 반이상 완성이 된것

추상클래스로 부터 상속받는 자손 클래스는 오버라이딩을 통해 조상인 추상클래스의 추상메서드를 전부 구현해주어야한다.

  • 추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
  • 구체화 : 상속을 통해 클래스를 구현하고, 확장하는 작업

추상클래스 예제

Unit 이라는 추상클레스에는 move 라는 기능을 선언해두고 이것을 상속받는 Marine, Tank, Marine, DropShip 을 구현하였다 .

java의 다형성에 따라서 Marine, Tank, Marine, DropShip는 전부 Unit이라는 것을 로 나타낼 수 있기 때문에

Unit [] group = new Unit[4]; 

에 전부 담을수 있다. 그리고 move 라는 메서드를 가지고 있기 때문에

group[i].move(100,200) 을 통하여 전부 구현이 가능하다.

모든 클래스의 Object로도 전부 담을수 있지만 move의 기능이 없기 때문에 move 부분을 호출할 수가 없다.


인터페이스(Interface)

일종의 추상클래스, 추상메서드를 갖지만 추상화 정도가 높아서 몸통을 갖춘일반 메서드 또는 멤버변수를 가질수 없다.

추상클래스 : 미완성 설계도

인터페이스 : 구현된것은 아무것도 없고 밑그림만 그려져 있는 기본 설계도

인터페이스 작성

interface에도 클래스 같이 접근 제어자로 public 또는 default 사용

interface interFaceName{
  public static final 타입_상수이름 = value;
  public abstract methodName(매개변수);
}

일반적인 클래스의 멤버와 인터페이스 멤버와의 다른점

- 모든 멤버변수는 public static final 이여야하며, 생략 가증 
- 모든 베서드는 public abstract이어야하며, 이를 생략 할 수 있다
  단 static메서드와 default메서드는 예외(JDK1.8 부터)

아래와 같이 맴버변수와 메서드를 정의하면 컴파일시에 주석과같이 자동으로 붙여준다.

interface PlayingCard{
  public static final int SPADE = 4;
  final int DIAMOND = 3; // public static final int 
  static int HEART = 2;// public static final int 
  int CLOVER = 1; // public static final int 

  String getCardKind() // public abstract String getCardKind();
}

1.8부터는 static메서드와 default 메서드의 추가를 허용하는 방향으로 변경됨

인터페이스 상속

클래스와 달리 다중 상속이가능하다.

여러개의 인터페이스를 상속 받으면 인터페이스들의 멤버로 갖게 된다.

다중 상속에서 인터페이스의 메서드 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야함

abstract class Fighter implements Fightable

인터페이스 장점

  1. 개발시간 단축
  2. 표준화가 가능
  3. 서로 관계없는 클래스에게 관계를 맺어 줄수 있다.
  4. 독립적인 프로그래밍이 가능하다.
class Unit{}

GroundUnit extends Unit{}
AirUnit extends Unit{}

Marine extends GroundUnit{}
SCV extends GroundUnit{}
Tank extends GroundUnit{}

Dropship extends AirUnit{}

위와 같은 클래스들이 있다면 기계화 유닛으로 수리에 대한 기능을 만들고 싶지만 scv, tank와 dropship 는 Unit이라는 공통의 조상을 가지고 있지만, Unit에 marine 이라는 클래스에도 영향을 받기 때문에 repair() 기능을 추가하지 못한다.

이러할 경우 인터페이스를 사용해서 클래스들에게 관계를 맺어 줄수 있다.

SCV extends GroundUnit implements Repairable{}
Tank extends GroundUnit implements Repairable{}
Dropship extends AirUnit implements Repairable{}

Repairable 인터페이스를 생성 해준다면

SCV, tank, Dropship의 instanceof(type 검사) 는 repairable로 나타낼 수 있다.

이러할 경우, SCV 클래스에

class SCV extends Units implements Repairable {
  ...
  void repair( Repairable r){
    if(r instanceof Unit){
      while( u.hitPoint!=u.MAX_HP){
                            u.hitPoint++;        
      }
      System.out.println( u.toString() + "수리가 끝남");
    }
  }
}

으로 구현이 가능하다.

scv.repair(tank);
scv.repair(scv);
scv.repair(dropship);

이렇게 지상유닛과 공중 유닛에 대한 관계없는 클래스들에게 관게를 맺어 줄수 있다는 장점이 있다.

'ProgramLanguage > Java' 카테고리의 다른 글

Java 와 객체 지향  (0) 2019.12.03
Java jar파일 만들기  (0) 2019.08.04
람다 식 Lambda Expression (1)  (0) 2019.06.09
익명클래스(Anonymous Class)  (0) 2019.06.08
Java OutOfMemoryError  (0) 2017.12.18

Lambda_expression(1)

자바의 정석의 책을 중심으로 정리하였습니다.

JDK1.8 부터 추가된 람다식(Lambda) 등장

Lambda expression이란 ?

메서드를 하나의의 식으로 표현한것.

함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.

메서드를 람다식으로 표현하면, 메서드의 이름과 반환값이 없어지므로, 람다식을 익명함수(anonymous function) 이라고도 한다.

int[] arr= new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random()*5)+1);

위의 람다식 (i) -> (int)(Math.random()*5)+1 을 메서드로 표현하면 다음과 같다

int method(){
  return (int)(Math.random()*5) +1;
}

lambda expression 작성하기

반환값이 있는 메서드의 경우, return문 대신 Expression으로 대신 할 수 있다.

식의 연살 결과가 자동적으로 반환값이 됨,

  • 중요한점은 '문장'이 아닌 식(Expression)이므로 끝에 ; 를 붙이지 않는다.

(int a, int b) -> { return a>b ? a:b ;} —> (int a, int b) -> a>b? a:b

문장(statement) 식 (expression)
return a>b ? a:b ; a>b? a:b

  • 람다식에 선언된 매개변수의 타입은 추론이 가능한 경우는 생략이 가능한 경우는 생략 가능
    람다식에 반환타입이 없는 이유는 항상 추론이 가능하기 때문이다.
    (int a, int b) -> a>b? a:b —> (a,b) -> a>b? a:b
    두 매개변수 중 어느 하나의 타입만 생략하는것은 허용되지 않는다.
  • 매개변수가 하나일 경우에는 괄호를 생략 가능하다.
    (a) -> aa —> a -> aa (int a) -> a/a —> int a-> a/a ( 에러 )
  • 괄호{ } 안의문장이 하나일 경우에는 괄호 생략 가능
    (String name, int i) -> { System.out.println(name+"="+ i ) ;} 은 다음과 같이 만들수 있다.
    (String name, int i) -> System.out.println(name+"="+ i ) 여기서는 ; 를 제거 해야한다.

함수형 인터페이스( functional interface)

자바에서 모든 메서드는 클래스 내에 포함되어야하는데, 람다식은 어떤 클래스에 포함되는 것일까??

람다식은 익명 클래스의 객체와 동등하다. 라고 되어있는데 무슨말일까??

package lambda_expression;

public class lambda01 {


    public static void main(String[] args) {

        //익명클래스의 객체의 생성
        MyFunction f1 = new MyFunction() {
            @Override
            public int max(int a, int b) {
                return a > b ? a : b;
            }
        };

        int big = f1.max(5, 3);//익명 객체의 메서드를 호출
        System.out.println(big);

        //람다식으로 표현으로 바꿈
        MyFunction f2 = (int a, int b) -> a > b ? a : b; // 익명 객체를 람다식으로 대체
        int big2 = f2.max(6,3);//익명 객체의 메서드 호출

        System.out.println(big2);
    }
}

결과

5
6

람다식과 동등한 메서드가 정의 되어 있는 것이어야한다.!!

그래야만 참조변수로 익명 객체의 메서드를 호출할 수 있기 때문이다.

MyFunction인터페이스를 구현한 익명 객체를 람다식으로 대체가 가능한 이유

: 람다식도 실제로는익명객체이고, MyFunction 인터페이스르 구현한 객체의 메서드 max()와 람다식의 매개변수의 타입과 개수 그리고 반환값이 일치 하기 때문이다.

람다식을 다루기 위한 인터페이스를 '함수형 인터페이스(functional interface) 라고 부르기로 함

람다식과 인터페이스의 메서드가 1:1 로 연결될 수 있기 때문

기존의 인터페이스의 메서드를 구현하여 사용하는 것은 아래와 같다.

 List<String> list = Arrays.asList("abc","aaa","bbb","ddd","aaa");
        Collections.sort(list, new Comparator<String>(){
            @Override
            public int compare(String s1, String s2) {
                return s2.compareTo(s1);
            }
        });

이것을 람다형식으로 변경해주면 ??

 List<String> list = Arrays.asList("abc","aaa","bbb","ddd","aaa");
Collections.sort(list,(s1,s2) -> s2.compareTo(s1));

이렇게 간단하게 사용할 수 있다.

Collections클래스의 sort 메서드와 Comparator 인터페이스를 살펴 보았다

public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }
    ....    
@SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }
  ...
}

Collections의 sort의 파라미터는 Compator 인터페이스이며,

Comparator인터페이스는 @FunctionalInterface 를 어노테이션으로 선언되어 있었다.

@FunctionalInterface
public interface Comparator<T> {
    /**
     * Compares its two arguments for order.  Returns a negative integer,
     * zero, or a positive integer as the first argument is less than, equal
     * to, or greater than the second.<p>
     ...
        */
    int compare(T o1, T o2);
  ...

//p 798

메서드의 매개변수가 @FunctionalInterface 로 정의된 타입의 인터페이스라면 메서드를 호출할때 람다식을 참조하는 참조변수를 매개변수로 지정해야한다는 뜻

void aMethod(Function f){
  f.myMethod();
}
...
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);

종합 한 코드를 보면 다음과 같다.

package lambda.example;


@FunctionalInterface
interface LambdaFunction {
    void run(); //public abstract void run()
}

public class LambdaEx1 {

  //매개변수의 타입이 LambdaFunction인 메서드
    static void execute(LambdaFunction f) {
        f.run();
    }
    //반환 타입이 LambdaFunction인 메서드    
    static LambdaFunction getMyFunction() {
        LambdaFunction f = () -> System.out.println("f3.run()");
        return f;
    }

    public static void main(String[] args) {
      //람다 식으로 LambdaFunction run()을 구현
        LambdaFunction f1 = ()->System.out.println("f1.run()");

        LambdaFunction f2 = new LambdaFunction(){//익명 클래스로 run()을 구현
            @Override
            public void run() {
                System.out.println("f2.run()");
            }
        };

        LambdaFunction f3 = getMyFunction();//이미 함수로 LambdaFunction의 run을 구현 
        f1.run();
        f2.run();
        f3.run();

        execute(f1);
        execute(()->System.out.println("run()"));
    }
}

'ProgramLanguage > Java' 카테고리의 다른 글

Java 와 객체 지향  (0) 2019.12.03
Java jar파일 만들기  (0) 2019.08.04
인터페이스와 추상클래스  (0) 2019.06.10
익명클래스(Anonymous Class)  (0) 2019.06.08
Java OutOfMemoryError  (0) 2017.12.18

+ Recent posts