[자바] - 새로운 문법

yeom yaloo·2023년 12월 11일
0

FISA

목록 보기
10/61
post-thumbnail

자바의 새로운 문법

[jdk1.5에서 등장한 enum]

1. 기존의 상수값 표현


class Color{

	public static final int RED = 1;
    public static final int YELLOW = 2;

}

2. 열거형 클래스

enum Color{
	RED, YELLOW
}
  • class 대신 enum 사용
  • 열거형 상수 선언(enumeration constants)
    • public static final 자동 반영
    • 콤마(,)로 구분
  • 고정 데이터를 사용하기 위해서 등장한 방식

3. enum 장점

  • 허용 가능 값들로만 제한할 수 있다.
  • 데이터 자체가 변수명(상수명)이다.
    • 명확해서 활용도가 높다.
  • 리팩토링이 수월해진다.
    • 내용 추가가 필요하더라도 enum 외에 수정할 필요가 없다.
    • 유형의 안정성을 향상시킬 수 있고 리팩토링시 변경의 범위가 최소화된다.

[제네릭]

1. 제네릭이란? (Generics)

  • 컴파일 과정에서 타입을 체크해주는 기능

2. 제네릭 사용 이유

컴파일 시 타입 체크를 하기 때문에 객체 안정성이 높아진다.
형변환이 용이해진다.

제네릭의 T와 E
List<E> E의 경우엔 배열처럼 사용되는 것들에 E가 사용되는 것이 적합
public class ClassName<T> { ... }: T의 경우엔 E를 사용하는 경우 예외의 모든 경우

3. 제네릭 클래스

4. 제네릭 인터페이스

5. 제네릭 메서드

6. 제네릭 와일드 카드


[jdk 1.8에 등장한 :: 더블 콜론]

1. 메서드 연산자 ::

  • jdk 1.8(자바 8)부터 추가된 메소드 연산자 ::
  • 해당 연산자를 람다를 통해 사용하면 파라미터의 중복을 막을 수 있다.

2-1. 도메인

/* DTO[Data Transfer Object]
 * 1. 기본 구조
 * 	- 멤버변수/기본생성자/생성자/getXxx/setXxx/toString()
 * 2. 참고
 * 	- 객체 생성시 다수의 멤버 변수가 존재하면 선별해서 변수값 초기화를 효율적으로 하기 위해서는
 * 	  builder pattern을 사용하면 효율적으로 사용 가능하다.
 * */

@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class People {

	private String name;
	private int age;

}

2-2. main method

package step01;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import model.domain.People;

public class DoubleColonTest {

	@Test
	public void test() {

		People p1 = new People("재석", 20);

		People p2 = new People("영자", 10);

		People p3 = new People("동엽", 30);

		System.out.println(p1);

		// step01 - API로 List<People> 객체로 자동 변환
		List<People> all = Arrays.asList(p1, p2, p3);

		/*
		 * [People(name=재석, age=20), People(name=영자, age=10), People(name=동엽, age=30)]
		 * List 객체가 보유한 toString()이 호출됐음을 알수 있다. 각각의 요소(데이터-객체)값을 출력할 때 각 요소별
		 * toString()가 호출이 됨
		 * 
		 * 즉, 리스트 객체의 toString()이 호출되고 또 각각의 리스트 안에 들어간 값을 호출할 때 해당 객체의 toString()이 호출된
		 * 형태
		 */
		System.out.println(all);


		/*step02 - 반복문으로 데이터를 출력
		 * People(name=재석, age=20) 
		 * People(name=영자, age=10) 
		 * People(name=동엽, age=30)
		 */
		System.out.println("step02: 반복문으로 데이터를 출력");
		for (People p : all) {
			System.out.println(p);
		}

		/*step03 - 반복문과 람다를 사용해서 데이터를 출력
		 * People(name=재석, age=20) 
		 * People(name=영자, age=10) 
		 * People(name=동엽, age=30)
		 */
		System.out.println("step03: 반복문과 람다를 사용해서 데이터를 출력");
		all.forEach(p -> System.out.println(p));

		
		/*step04 - 람다와 double colon으로 데이터 출력
		 * People(name=재석, age=20) 
		 * People(name=영자, age=10) 
		 * People(name=동엽, age=30)
		 */
		System.out.println("step04: 람다와 double colon으로 데이터 출력");
		all.forEach(System.out::println);
	}
}
  • test 메서드를 통해서 해당 작업의 차이점을 코드로 알아볼 수 있다.

[jdk 1.8에 등장한 람다식]

[람다와 관련한 인터페이스, 미완성 메서드 구현, 익명 이너 클래스, @FunctionalInterface와 람다]

1. 람다란?

  • 람다는 jdk1.8 부터 추가된 표현식이다.
  • 람다 표현식: @FunctionalInterface의 구현체로 생성되는 인스턴스이다.
  • 메서드를 직접 정의(구현)하지 않고 하나의 식으로 표현한 것을 람다식이라고 한다.

2. 람다가 필요한 이유?

  • 실행되는 메서드의 구현 코드를 간소화시킬 수 있기 때문에 필요하다.
  • 가독성과 유지보수를 향상시킬 수 있기 때문에 람다가 필요하다.
  • 타 언어와의 문법 경계성이 최소화되기 때문에 람다가 필요하다.
    • 파이썬의 람다
    • 자바스크립트의 화살표 함수
  • 동적 로직을 바로 적용할 수 있어서 람다가 필요하다.

3. *.java 종류

  • 인터페이스와 람다를 이용한 @FunctionalInterface을 알아보기 전에 .java 파일의 종류를 빠르게 알아보자.

*.java 종류
1. 순수 자바 클래스
2. enum - 데이터 자체를 상수화해서 명확성을 부여한 설정
3. interface
ㅤ3-1. 스펙
ㅤ3-2. 구조
ㅤㅤ- 상수 / 주석 / 메서드 선언구
ㅤ3-3. 강제사항
ㅤㅤ- 해당 인터페이스에 정의한 메서드가 있다면 이를 구현하는 클래스에서는 스펙에 맞게 메서드를 재정의해야 한다.
ㅤ3-4. 대표적인 구조
ㅤㅤDB 벤더사에서 db 판매를 위한 지원
ㅤㅤㅤ- db driver
ㅤㅤdb 접속 객체 사용 관점(API 사용자 관점)
ㅤㅤㅤ- 연결을 위한 메서드인 getConnection()의 구현 코드를 알 필요가 없음
ㅤㅤgetConnection() 제공자 관점(API 제공자 관점)
ㅤㅤㅤ- db 벤더사가 getConnection()을 제공할 때 구현을 하지 않은 채로 미완성 메서드로 이를 제공하고 있다. (= 스펙 제시)
ㅤㅤㅤ- 이렇게 메서드 껍데기만 제공하는 이유는 특정지어서 메서드를 구현하면 달라지는 데이터베이스 종류마다 해당 메서드의 내용을 변경해서 제공해야하기 때문이다 그렇기 때문에 사용하고자하는 데이터베이스에 상관 없이 껍데기만 제공해서 구현부에서 이를 채워서 사용자가 사용할 수 있게 했다.
ㅤㅤㅤ- 또한 하위 클래스에서 해당 메서드를 재정의를 꼭 해야 한다.
public abstract Connection getConnection(String url, String id, String );
ㅤㅤㅤ- 구현부 없이 스펙만 제시하고 있다.
ㅤ- 특별한 기능을 보유한 마킹 interface
4. servlet

[인터페이스 사용 방법]

1. 인터페이스의 기본 사용법

  • 주의해야 하는 점
    • 인터페이스의 경우엔 미완성 코드이기 때문에 인터페이스를 이용해서 객체를 생성해 사용할 수 없다.
    • 인터페이스는 꼭 구체(구현 클래스)가 필요하며 인터페이스를 타입으로 구체 클래스를 생성할 수 있다.

1-1. 인터페이스 작성 코드

public interface PeopleInfo {

	
	//상수, 주석과 함께 메서드 선언부가 필요
	public final String COMPANY_NAME ="fisa";
	
	/* "회사명 본인이름"을 결합해서 반환 
	 * 미완성 메서드로 하위클래스에서 재정의를 해야 한다.
	 * */
	public abstract String getPeopleInfo();
	
}
  • 기본 껍데기에 해당하는 인터페이스를 만든다.
  • 해당 작업에서 abstract 메서드는 해당 인터페이스를 구현해서 사용하는 구체 클래스에서 해당 메서드를 꼭 재정의 해야한다. = 강제적인 재정의 작업이 필요!

1-2. 인터페이스 구현 코드

public class MyPeople implements PeopleInfo {

	@Override
	public String getPeopleInfo() {
		return COMPANY_NAME + " 이름";
	}

	@Test
	public void test() {

		PeopleInfo myPeopleInfo = new MyPeople();

		System.out.println(getPeopleInfo());

	}
}
  • 인터페이스를 구현한 구체 클래스인 MyPeople 클래스가 abstract 메서드를 재정의하지 않으면 에러가 난다.

2. 익명 이너 클래스를 이용한 방법

  • 기존 인터페이스를 구현하는 방식이 아닌 익명 이너 클래스를 이용해서 인터페이스의 미완성 메서드를 재정의해보자
import org.junit.Test;

/* 인터페이스
 * 1. 상수와 미완성 메서드로 구성
 * 2. 인터페이스는 미완성 코드이기 때문에 객체 생성이 불가
 * 3. 타입으로는 사용이 가능하다.
 * 
 * 즉, 타입으로는 사용이 가능하지만 생성자가 없기 때문에 객체 생성은 불가하다.
 * 
 * */

public class MyPeople2 {

	// interface를 완벽하게 구현하는 이름없는 클래스 개발 = 익명 inner class
	@Test
	public void step01() {

		// PeopleInfo peopelInfo = new People(); 은 불가!
		PeopleInfo p1 = new PeopleInfo() {

			@Override
			public String getPeopleInfo() {

				return null;
			}
		}; // 세미콜론 생략 불가

		PeopleInfo p2 = new PeopleInfo() {

			@Override
			public String getPeopleInfo() {

				return null;
			}
		}; // 세미콜론 생략 불가

		System.out.println(p1);
		System.out.println(p2);

	}

}
  • 인터페이스는 타입으로 사용이 가능하지만 인터페이스 내에는 생성자가 없어서 객체 생성이 불가하다.
  • 인터페이스를 구현하는 구현체를 사용해서 미완성 메서드를 재정의하는 방법으로 사용하는 것이 일반적이라면 익명 이너 클래스를 사용해서 구현체 코드 작성 없이 익명 이너 클래스로 미완성 메서드를 재정의해서 사용할 수 있게 했다.
  • 익명 inner class는 즉, 구체 없이 익명 inner class를 이용해서 재정의를 통해 사용할 수 있게 한다.

출력 결과
step01.MyPeople2$1@799d4f69
step01.MyPeople2$2@49c43f4e

  • 기존 자바 프로젝트의 경우엔 bin 디렉토리 하위에 바이트 코드가 생성될 것이다.
  • 그러나 메이븐 프로젝트의 경우엔 target 파일 하위에 해당 바이트 코드가 생성됨을 주의하자.
  • 기존 java 클래스의 경우엔 $(달러사인) 없이 *.class이 생성된다.
  • 그러나 익명 inner class의 경우엔 객체이름$익명이너클래스명.class로 바이트 코드가 생성된다.

[@FunctionalInterface의 도입]

1. 인터페이스를 @FunctionalInterface로 도입

// 연산 + 람다식을 사용할 인터페이스
@FunctionalInterface
public interface Calc {
	/* 두개의 데이터를 받아서 연산로직후 반환 로직으로 재정의를 해라 */
	int calculation(int v1, int v2); // public abstract 자동 적용

}
  • @FunctionalInterface 애너테이션을 이용해 기능적 인터페이스로 사용할 것을 선언한다.
  • 해당 애너테이션은 명시적인 의미이기 때문에 꼭 붙이지 않아도 된다. (권장 사항이다.)
  • 이를 붙이면 메서드 하나만 허용한다. (중요~)

2. 인터페이스와 람다사용 코드 예제

public class CalcClass {

	@Test
	public void step01() {

		// 익명 클래스 기반의 개발
		Calc c1 = new Calc() {

			@Override
			public int calculation(int v1, int v2) {
				return v1 + v2;
			}
		};

		System.out.println(c1);
		System.out.println(c1.calculation(1, 2));

	}

	@Test
	public void step02() {
		System.out.println("-- 1. 더하기 --");
		Calc addCalc = (int v1, int v2) -> v1 + v2; // 중괄호가 없어서 return 생략
		System.out.println(addCalc.calculation(20, 21));

		System.out.println("-- 1. 더하기(중괄호 사용과 return) --");
		Calc addCalc2 = (a, b) -> {
			return a + b;
		};
		System.out.println(addCalc2.calculation(2, 3));

		System.out.println("-- 2. 빼기 --");
		Calc calc2 = (int v1, int v2) -> v1 > v2 ? v1 - v2 : v2 - v1;
		System.out.println(calc2.calculation(2, 1));
		System.out.println(calc2.calculation(1, 3));

		System.out.println("-- 3. 곱하기(타입 없이) --");
		Calc calc3 = (a, b) -> a * b;
		System.out.println(calc3.calculation(2, 2));

	}

}
  • step01()의 경우엔 기존 인터페이스를 익명 이너 클래스를 사용해서 객체 생성 없이 바로 메서드 재정의를 통해 사용할 수 있게 하는 코드이다.
  • Calc addCalc = (int v1, int v2) -> v1 + v2; : step02()의 경우엔 람다를 이용해서 미완성 메서드를 재정의하고 있다.
  • System.out.println(calc3.calculation(2, 2));: 재정의한 메서드를 사용하고 있다.

[람다와 관련된 정리]

  • 람다를 사용하기에 앞서서 우리는 인터페이스와 이를 구현한 구체를 살펴볼 수 있다.
  • 이때 일반적으로 인터페이스를 구현한 구현체를 통해서 해당 메서드를 강제로 재정의해서 미완성된 메서드를 재정의한 코드로 불러 사용해야 했다.
  • 인터페이스는 타입으로는 사용이 가능하지만 생성자가 없어서 객체 생성에는 사용할 수 없다.
  • 그럼 인터페이스에 있는 미완성 메서드를 사용하기 위해서 우리는 구현체 클래스 작성과 미완성 메서드 강제 재정의를 해야지만 할까?
  • 전혀 아니다 이를 구체 클래스 없이도 사용할 수 있게 익명 이너 클래스를 이용해서 해당 메서드를 재정의해 사용할 수 있다.
  • 그럼 우린 이 두가지 방법만을 이용해서 해당 미완성 메서드 코드를 사용할 수 있을까?
  • 아니다 이는 또 람다로 처리할 수 있는데 람다와 인터페이스를 같이 사용하고자 하면 인터페이스 내에 @FunctionalInterface 애너테이션을 달아주어야 한다.
  • 이때 해당 애너테이션을 달면 미완성 메서드는 한가지만 작성이 가능하고, 이 애너테이션을 사용하는 것은 권장사항이라 달지 않더라도 크게 문제가 되지 않지만 권장이니만큼 미완성 메서드를 람다와 사용할 경우라면 해당 인터페이스에 해당 애너테이션을 꼭 명시해주도록 하자!

[Stream API]

1. 스트림 API란?

  • 배열이나 컬렉션처럼 데이터 그룹을 간단하고 효율적으로 처리할 수 있도록 jdk8(= jdk1.8)버전부터 추가된 기능이다.
  • 내부 반복 기능이다.
  • 데이터베이스와 같은 연산 수행도 가능하다.

2. 스트림 데이터의 특징

  • 처리 과정에서 임시로 존재한다.(스트림에 요소=데이터가 저장되지 않는다.)
    • 배열 또는 IO와 같은 소스의 요소를 계산 작업의 파이프라인을 통해 전달한다.
  • 작업 후엔 자동 소멸된다.
  • 멀티 쓰레드 코드 작성을 따로 하지 않아도 병렬 처리를 지원하여 대량의 데이터 처리를 가능하게 한다.
  • 데이터 소스 원본의 변경 없이 데이터 처리 작업을 수행한다.
    • 데이터 구조로 표현이 불가하다.

3. 스트림 사용 단게

  1. Stream 생성 collectionName.stream()
    • 스트림은 최종 연산 수 사라지기 때문에 다시 사용하기 위해서는 스트림을 재생성 해주어야 한다.
  2. Stream 중간 연산
    • 중간 연산은 여러번 가능하다.
  3. Stream 최종 연산
    • 최종 연산 후 Stream은 삭제

public class StreamAPI1 {

	@Test
	public void step01() {

		List<String> datas = Arrays.asList("a", "b", "c", "d", "e");

		System.out.println(datas);
		System.out.println(datas.get(0));
		datas.forEach(data -> System.out.println(data));

		System.out.println("-- lab2 : 더블 연산자로 출력");
		datas.forEach(System.out::println);

		System.out.println("-- lab3 : b를 제외한 출력(=조건 기능 추가)");
		datas.forEach(data -> {
			if (!data.equals("b")) {
				System.out.println(data);
			}
		});

		/*
		 * data들을 stream 객체화해서 필터링 후 필터링된 데이터를 반복해서 출력 . : 접근 연산자, dot 연산자 접근 연산자가 지속적으로
		 * 나오는 문법을 체이닝이라고 한다. 메서드를 지속적으로 호출시에는 체이닝 메서드 호출
		 */
		System.out.println("-- lab4 : Stream API를 활용한 조건 기능 추가");
		datas.stream().filter(data -> !data.equals("b")).forEach(System.out::println);

		// 기본 타입과 객체 타입 다름 - java.util.List는 객체들만 저장 및 관리한다.
		// 1~5까지 데이터 저장
		// 3을 제외한 데이터만 람다식 스트림으로 출력
		/*
		 * 1,2,3,4,5: 기본타입 값으로 코딩, 컴파일 후 실행시 Integer 자동변화 autoboxing int -> Integer이 1:1
		 * 지원 클래스
		 */
		List<Integer> data2 = Arrays.asList(1, 2, 3, 4, 5);// autoboxing

		data2.stream().filter(data -> !data.equals(3)).forEach(System.out::println);

		/*
		 * 해당 작업은 String 3이랑 같은 경우이기 때문에 3이 출력이 된다.
		 * 
		 * 하위 클래스들이 객체의 내용값 비교로 재정의된다. - 객체 타입 먼저 확인하고 동일한 타입인 경우에만 동일 타입의 객체들간의 내용을
		 * 비교한다. - public boolean equals(Object value){ }
		 */
		System.out.println(" String과 Integer은 다른 타입으로 비교가 부적합하다. (에러는 아님)");
		data2.stream().filter(data -> !data.equals("3")).forEach(System.out::println);

		/*
		 * autoboxing : 기본 타입을 객체 타입으로 변경 시키는 작업 Integer i = 3; -> Integer i = new
		 * Integer(3);
		 * 
		 * unboxing : 객체 타입을 기본 타입으로 빼오는 작업 Integer, ... = wrapper class int i = new
		 * Integer(3); -> int i = (new Integer(3)).intValue();
		 * 
		 */
		System.out.println("autoboxing & unboxing");
		data2.stream().filter(data -> data != 3).forEach(System.out::println);

	}

}
package step02.stream.api;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Test;

public class StreamAPI2 {

	// @Test
	public void mapTest() {

		Map<String, String> datas = new HashMap<String, String>();

		datas.put("a", "재석");
		datas.put("b", "연어");
		datas.put("c", "종원");
		datas.put("d", "동엽");

		// key 를 모르는 상태로 value값 확인 가능
		datas.forEach((k, v) -> System.out.println("key: " + k + " value: " + v));

	}

	@Test
	public void streamTest() {
		List<String> datas = Arrays.asList("1", "2", "3", "4", "5");

		datas.stream().forEach(System.out::println);
		datas.stream().forEach(a -> System.out.println(a + 1));

		/*
		 * <? super T>: T를 기준으로 T의 상위타입 문자열을 숫자로 변환(int) : parseInt
		 */
		int r = datas.stream().mapToInt(data -> Integer.parseInt(data)).sum();
		System.out.println(r);

		int r2 = datas.stream().mapToInt(Integer::parseInt).sum();
		System.out.println(r2);

		
		double d1 = datas.stream().mapToDouble(Double::parseDouble).sum();
		System.out.println(d1);

	}
}

[Optional]

1. Optional이란?

  • null처리를 대신하기 위해서 만들어진 코어 라이브러리 데이터 타입이다
  • 객체를 보유하고 있는 컨테이너 기능을 하고 있다.
  • null이나 null이 아닌 값을 담을 수 있는 클래스이다.

2. Optional 객체 생성 메서드

  • Optional.empty(): 빈 Optional 객체 생성시에 사용한다.
  • Optional.of(): 값이 null이 아닌 경우에 사용한다.
  • Optional.ofNullable(Value): 해당 값이 null인지 아닌지 확실하지 않은 경우에 사용한다.
profile
즐겁고 괴로운 개발😎

0개의 댓글