- Interface : 인터페이스를 사이로 앞, 뒤의 개발 코드가 서로 통신하는 접점. 앞, 뒤의 통신 규약 → 동시 개발 가능
interface Practice {
// 상수
(final/static : 지우라고뜸) 타입 상수명(대문자 convention) = 값;
String HI = "Hi~";
// 추상 메서드
List<String> findAllName();
// Default 메소드
default 타입 메소드명(파라미터,...) {...}
default void printHi() {
System.out.println(HI);
}
// static 메소드
static void printHi() {
System.out.println(HI);
}
}
1) 상수만 가능 → 클래스 로드 시점에 초기화
public static final 가 컴파일 시점에 붙는다.
2) 추상메서드로 구현체에게 구현 강제
3) 기본 메서드 제공
→ 자바는 단일 상속, 다중 구현을 제공 → 만약 두개의 인터페이스를 구현했는데, 두 인터페이스의 메서드 명이 똑같다면 어떻게 되나?? → 충돌나는 메서드 시그니처의 오버라이딩을 강제화!
→ public 생략시 컴파일 과정에서 자동으로 붙여줌
4) Static메소드 → 헬퍼 또는 유틸리티 메소드를 제공할 때 사용
public interface Practice {
String HI = "HI~"; // 상수 정의 가능 컴파일 시 앞에 static finald이 붙는다고 생각하기!
default void printHi(){
//java 8 이상부터 기본 메소드 사용가능
System.out.println("default "+ HI);
}
static void printHI(){
//static 메소드 사용가능
System.out.println("static "+ HI);
}
}
public class Example implements Practice{
public void print(){
System.out.println(HI);
}
public void defaultMethod(){
//원래 인터페이스는 함수 껍대기만 있기 때문에 구현해서 완성 시키는 것 이였는데, 이렇게 기본메서드를
//제공한다.
printHi();
}
public void staticMethod(){
Practice.printHI();
}
public static void main(String[] args) {
Example ex = new Example();
ex.print();
ex.defaultMethod();
ex.staticMethod();
// HI~
// default HI~
// static HI~
}
}
1) 인터페이스 -> 다중 구현 / 추상클레스 -> 단일 상속
2) 인터페이스 -> 어떤 공통된 속성을 가지고 구현한다 / 추상클레스 -> 속성을 상속받아 확장시키는 것
3) 인터페이스의 접근제어자는 public만 가능 -> 공개 목적이기 때문!!
- Java에서는 같은 타입끼리의 연산을 제공한다.
- Ex) Int + Int = true ;
Ex) int + Double = false ; -> 이럴 때 형변환 필요하다!!!
- 이런식으로 작은 데이터 타입 -> 큰 데이터 타입으로의 변환은 자동으로 형변환이 일어난다.
- long 타입의 메모리는 8byte인데 float로 자동 형변환이 가능한 이유는 float가 표현범위가 더 크기 때문이다.
- byte 타입 -> char 타입 : 자동 형변환 불가
- float 타입 -> long 타입 : 자동 향변환 불가
- 인터페이스로 보는 자동 형변환
-> 인터페이스 변수 = 구현객체; ← 자동 타입 변환
-> Interface clazz = new InterfaceImplementClass();
-> List< > arr = new ArrayList< >( ); 이것 또한 List인터페이스로 자동 형변환인가?
👉 큰 데이터 타입 = 작은 데이터 타입 -> 자동 형변환이 이루어짐
상위 인터페이스, 클래스, 추상 클래스로 Upcasting 가능
→ 모든 클래스는extends Object
가 생략되어있다.
→ 모든 클래스는 Object로 Upcasting 가능
int i = 10;
double j = 5.5;
double result = i + j; // 15.5
@FuntionalInterface
애노테이션을 인터페이스에 선언하면 컴파일 시점에서 추상메소드를 하나만 갖는지 체크해 준다.
@FunctionalInterface
public interface Sum {
int intSum(int x, int y);
}
- 메소드를 하나의 식으로 표현한 것( 쉽게 이야기 하자면 )
List<String> list = new ArrayList();
list.add("Element1");
list.add("Element2");
list.add("Element3");
list.forEach(x -> System.out.println(x))
// 위 코드는 list.forEach(System.out::println) 으로 축약할 수 있음
// 원래라면 for문 돌려야 함!..
- 스트림은 데이터를 변경하지 않는다.
- 스트림 연산이 끝난 후 재 사용할 수 없다.
- 0 ~ N 개의 중간 연산과 1개의 종료 연산으로 구성.
- 내가 사용했을 때의 경험은 중간 filter,map...등등이 여러개 붙은 후 forEach()로 마무리 했던 경험
- Stream을 리턴
- Stream을 리턴하지 않는다.
- 필터링 :
filter
,distinct
- 변환 :
map
,faltMap
- 제한 :
limit
,skip
- 정렬 :
sort
- 요소 출력 :
forEach
- 요소 검색 :
findFirst
,findAny
- 요소 통계 :
count
,min
,max
- 요소 연산 :
sum
,average
- 요소 수집 :
collect
- NPE( Null Point Exception ) 예외를 Optional이 제공하는 메소드로 간단히 회파할 수 있다.
- Optional 개념 쉬운 예제로 설명한 블로그 : https://ynzu-dev.tistory.com/entry/JAVA-Optional-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%98%88%EC%A0%9C
- NPE는 물론, NoSuchElementException이 발생함.
- 잘못된 Optional사용으로 새로운 문제들이 발생함
- 코드의 가독성을 파괴
- 시간, 공간적 비용이 증가함
- Null을 반환하면 오류가 발생할 가능성이 매우 높은 경우에 "결과없음"을 명확하게 드러내기 위해 메소드의 반환 타입으로 사용되도록 매우 제한적인 경우로 설계 됨.
🚨 Optional은 메소드의 반환형으로만 사용가능!!!!! 🚨
Optional<String> opt = Optional.ofNullable("Optional은 Wrapper Class");
System.out.println(opt.get());
- empty( )
import java.util.Optional;
public class Example {
Optional<String> empty = Optional.empty();
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.empty.isPresent()); // false
}
}
- of( )
🚨 of( ) 는 null이 아님을 확신할 때 사용.
import java.util.Optional;
public class Example {
Optional<String> empty = Optional.of("assert NotNull");
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.empty.isPresent());
}
}
- ofNullalbe( )
import java.util.Optional;
public class Example {
Optional<String> empty = Optional.ofNullable(null);
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.empty.isPresent());
}
}
- ifPresent( )
👉 Optional에서 꺼낸 객체가 존재한다면, 구문수행
import java.util.Optional;
public class Example {
String name = null;
Optional<String> opt = Optional.ofNullable(name);
public static void main(String[] args) {
Example ex = new Example();
ex.opt.ifPresent(s -> System.out.println(s)); // null이기 때문에 아무 값도 없음
}
}
- OrElse( )
👉 Optional에서 꺼낸 객체가 존재한다면 꺼내고 그렇지 않으면,orElse의 인자값을 반환
import java.util.Optional;
public class Example {
String name = null;
Optional<String> opt = Optional.ofNullable(name);
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.opt.orElse("값이 없어요!!"));//값이 없어요!! 출력
}
}
- OrElseGet( )
👉 OrElse( ) 와 비슷하지만, 인자값으로 람다 표현식의 결과값을 출력
import java.util.Optional;
public class Example {
String name = null;
Optional<String> opt = Optional.ofNullable(name);
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.opt.orElseGet(()->"값이 없는데요?"));// 값이 없는데요? 출력
}
}
- orElseThrow( )
👉 Optional에서 꺼낸 객체가 존재한다면 꺼내고, 그렇지 않다면? Exception 던지기
import java.util.Optional;
public class Example {
String name = null;
Optional<String> opt = Optional.ofNullable(name);
public static void main(String[] args) {
Example ex = new Example();
System.out.println(ex.opt.orElseThrow(IllegalAccessError::new));//IllegalAccessError터짐
}
}
mport java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Example {
List<Person> person = Arrays.asList(
new Person("park",20),
new Person("kim",30),
new Person("Lee", 40)
);
List<String> personName = person.stream().map(Person::getName).collect(Collectors.toList());
public static void main(String[] args) {
Example ex = new Example();
ex.personName.stream().forEach(System.out::println);
}
}
👉 이름과 나이를 가진 Person클레스를 객체로 가진 List 타입의 person에서 Name필드만 뽑아와서 따로 List로 저장해주는 부분
👉 원래 같았으면 for 반복문을 person.length만큼 돌려주면서 get(i).getName()해온 것을 String 타입을 가진 personName List에 add 해줘야 하는데 코드도 길어지고, 가독성이 떨어질 수 있다.
👉 이러한 Stream을 사용함으로써 간단한 코드 한줄로 위의 반복작업을 간단하게 해결할 수 있다.
- 정수형 문자 배열에서 중간 수를 가져오기
- 만약 문자열의 길이가 짝수일 경우 두개를 가져오게 된다.
import java.util.Arrays;
class Solution {
//가운데 글자 가져오기
// 가운데 글자를 가져오기
// 단어 길이가 홀수 = 가운데 한글자만 5 -> 3 7 -> 4 9 -> 5
// 단어 길이가 짝수 = 가운데 두글자만
public String solution(String s) {
char[] arr = s.toCharArray();
String answer;
if(arr.length%2==0){
char a = arr[arr.length/2-1];
char b = arr[(arr.length/2)];
answer = String.valueOf(a)+String.valueOf(b);
}else{
char a = arr[(arr.length/2)];
answer = String.valueOf(a);
}
return answer;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.solution("qwer"));
}
}
- 정수형 배열이 주어진다.
- [1,1,3,3,0,1] -> [1,3,0,1] 이 출력되어야 하며 순서가 보장되어야 함
- Set자료구조는 못쓰겠구나~
import java.util.*;
public class Solution {
//0~9까지의 숫자로 이루어진 int 배열 arr이 주어진다.
// 오케이 확인
public List<Integer> solution(int []arr) {
List<Integer> answer = new ArrayList<>();
for(int i=0;i<arr.length-1;i++){
if(i==0){
answer.add(arr[i]);
}
if(arr[i]!=arr[i+1]){
answer.add(arr[i+1]);
}
}
return answer;
}
public static void main(String[] args) {
Solution sol = new Solution();
sol.solution(new int[]{1,1,3,3,0,1,1}).stream().forEach(s-> System.out.println(s));
}
}
- 중복되지 않는 자연수를 가진 정수형 배열과, 나눌 자연수가 주어진다.
- 나누는 수로 나눠떨어지는 숫자들을 오름차순으로 출력하기
- 만약 나눠 떨어지는 숫자가 없다면 [-1] 반환하기
import java.util.Arrays;
class Solution {
//arr 의 각 요소중에서 divisor로 나눠떨어지는 값을 오른차순으로 정렬한 배열을 반환해라
public int[] solution(int[] arr, int divisor) {
int count = 0;
for(int i=0;i<arr.length;i++){
if(arr[i]%divisor==0){
count++;
}
}
if(count==0){
return new int[]{-1};
}
int [] answer = new int[count];
for(int j=0;j<arr.length;j++){
if(arr[j]%divisor==0){
answer[count-1] = arr[j];
count--;
}
}
Arrays.sort(answer);
return answer;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(Arrays.toString(sol.solution(new int[]{2,36,1,3},1)));
}
}