1.  Switch 문

🔅 단 하나의 조건식으로 많은 경우의 수를 처리할 수 있고 표현도 간결

수행 절차

 

  • 조건식을 계산한다.
  • 조건식의 결과와 일치하는 case문으로 이동
  • 이후의 문장들을 수행
  • break 문이나 switch문의 끝을 만나면 switch문 전체를 빠져나감
💡  POINT

     -  break문을 생락하면 case문 사이의 구분이 없어지므로 다른 break문을 만나거나 switch문 블럭 {} 끝을 만날 때까지 나오는 모든 문장들을 수행

    -  제약조건
   
   a.
조건식 결과는 정수 또는 문자열

       b. case문의 값은 정수 상수만 가능, 중복 X

    -  if문 처럼 중첩 가능

switch (조건식) {
    case 값1 :
        // 조건식의 결과가 값1과 같을 경우 수행될 문장들
        //...
        break;
    case 값2 :
        // 조건식의 결과가 값2과 같을 경우 수행될 문장들
        //...
        break;
    default :
        // 조건식의 결과와 일치하는 case 문이 없을 때 수행될 문장들
        //...
        break; // 생략가능.
}

 

조건식이 정수인 경우
public static void main(String[] args) {
    // 일년 동안 읽은 책 수에 따라 멘트를 출력합니다.
		
    int book = 5;
    switch (book/10) {
	    case 0 : // 10권 미만
		    System.out.println("조금 더 노력 하세요!");
		    break;
		case 1 : // 10이상 20권 미만
		    System.out.println("책 읽는 것을 즐기는 분이시네요!");
		    break;
		case 2 : // 20이상 30권 미만
		    System.out.println("책을 사랑하는 분이시네요!");
		    break;
		default : // 30권 이상
		    System.out.println("당신은 다독왕입니다!");
		    break; // 생략가능
    }
}

 

조건식이 문자열인 경우
public static void main(String[] args) {
		
    String medal = "Silver";
		
	switch(medal) {
	    case "Gold":
		    System.out.println("금메달 입니다.");
		    break;
		case "Silver":
		    System.out.println("은메달 입니다.");
		    break;
		case "Bronze":
		    System.out.println("동메달 입니다.");
		    break;
		default:
		    System.out.println("메달이 없습니다.");
		    break;
    }
}

 
​응용 예제
public static void main(String[] args) {
		
    Scanner scanner = new Scanner(System.in);
	System.out.print("월을 입력해 주세요 >>> ");
	int month = scanner.nextInt();
	int day;
		
	switch(month) {
//		case 1: case 3: case 5: case 7: case 8: case 10: case 12:
		case 1, 3, 5, 7, 8, 10, 12: // case가 여러가지인 경우 생략 가능.
			day = 31;
		    break;
		case 4: case 6: case 9: case 11:
			day = 30;
			break;
		case 2:
			day = 28;
			break;
		default:
			day = 0;
			System.out.println("존재하지 않는 달 입니다.");
	}
		
	System.out.println(month + " 월은 " + day + " 일까지 있습니다.");
	scanner.close();
}
 
public static void main(String[] args) {
    /* Java 12부터 개선된 switch 문을 지원 */
    Scanner scanner = new Scanner(System.in);
	    
	System.out.print("월을 입력해 주세요 >>> ");
	int month = scanner.nextInt();
	int day;
	    
	switch (month) {
	    
	    case 1, 3, 5, 7, 8, 10, 12 -> day = 31;
	    case 4, 6, 9, 11 -> day = 30;
	    case 2 -> day = 28;
	    default -> {
	    	day = 0;
	    	System.out.println("존재하지 않는 달입니다.");
	        }
	}
	    
	System.out.println(month + "월은 " + day + "일까지 있습니다.");
	scanner.close();
}

 

public static void main(String[] args) {
		/* 사용자에게 성적을 입력받아
		 * switch문을 사용해서 학점을 출력하는 코드를 완성하세요.
		 * 입력은 0 ~ 100까지 입력이 됩니다.
		 * 기준은 아래와 같습니다.
		 * A : 90 ~ 100
		 * B : 80 ~ 89
		 * C : 70 ~ 79
		 * D : 60 ~ 69
		 * F : 0 ~ 59 */
		
		Scanner scanner = new Scanner(System.in);
		int grade;
		String credit;
		
		System.out.print("성적을 입력하세요 >> ");
	    grade = scanner.nextInt();
	    
	    // 'int/int' 결과는 int 이기 때문에 '88/10'은 8.8이 아닌 8이 됨.
	    switch(grade/10*10) {
	    case 90,100: // 90 ~ 100
	        credit = "A";
	        break;
	    case 80:
	    	credit = "B";
	    	break;
	    case 70:
	    	credit = "C";
	    	break;
	    case 60:
	    	credit = "D";
	    	break;
	    default :
	    	credit = "F";
	    	break;	    	    	
	    }
	    
	    System.out.println("입력하신 성적은 " + grade + "점이고, 학점은 " + credit + "입니다.");
	    scanner.close();
		

	}
 


2. 반복문

💡 어떤 작업이 반복적으로 수행되도록 할 때 사용

💡 종류 ; for문, while문, do-while문

💡 조건식의 결과가 true이면 참, false면 거짓으로 간주

 

1) for문

 

✓ 반복 횟수를 알고 있을 때 적합

 

  👾  실수 사용 x 정수만 사용하기 ▶️ 계산이 부정확하기 때문

  👾  초기화, 조건식, 증감식 모두 생략 가능

            ▶️  조건식이 생략된 경우, 참으로 간주되어 무한 반복문이 된다.

            ▶️  대신 블럭{} 안에 if 문을 넣어서 특정 조건을 만족하면 for 문을 빠져 나오게 해야 한다.

 

for문의 구조
for (초기화; 조건식; 증감식) {
    // 조건식이 참일 때 수행될 문장들을 적는다.
}

 

for문 수행 순서

 

  📍  초기화 ▶️ 조건식 ▶️ 수행될 문장 ▶️ 증감식 ▶️ 조건식 ▶️ 수행될 문장 ▶️ 증감식 ...

public class MyFor_02 {
	public static void main(String[] args) {
		/* for문의 순서 */
		int sum = 0; // 총 합을 담을 변수. 초기화가 꼭 필요.
		// for (초기값; 조건식; 증감식)
		for (int i = 1; i > 0; i++) { //1부터 10까지의 합
			System.out.println("i = " + i + " sum = " + (sum += i));
			
			// 1) 초기값 실행
			// 2) 조건식 확인
			// 3) 조건식이 참이면 명령문 실행
			
			// 4) 증감식 실행
			// 5) 조건식 확인
			// 6) 조건식이 참이면 명령문 실행
			// 4) 5) 6) 반복
		}	
			// System.out.println(i); // i는 for문에서만 유효
		{ 
			int tmp = 12;
		    System.out.println(tmp);
		}    	
	}
}
 

    변수 i에 1을 저장한 다음, 매 반복마다 i의 값을 1씩 증가시킨다.

    그러다가 i의 값이 5를 넘으면 조건식 'i <= 5'가 거짓이 되어 반복을 마치게 됨

    i의 값이 1부터 5까지 1씩 증가 하니까 모두 5번 반복한다.

A. 초기화

· 반복문에 사용될 변수를 초기화하는 부분이며 처음에 한번만 수행.

· 둘 이상의 변수가 필요할 때 콤마 ',' 를 구분자로 변수를 초기화 (단, 두 변수의 타입은 같아야)

     ex. for (int i=1, j=0; i <= 10; i++) {...

 

B. 조건식

· 조건식의 값이 참이면 반복을 계속하고, 거짓이면 반복을 중단하고 for문을 벗어난다.

 

C. 증감식

· 반복문을 제어하는 변수의 값을 증가 또는 감소시키는 식.

· 콤마 ',' 를 이용해서 두 문장 이상을 하나로 연결해서 쓸 수 있다.

    ex. for (int i =1, j=10; i <= 10; i++, j--) {...

public class MyFor_03 {

	public static void main(String[] args) {
		/* for문 외부에 변수 선언 할 경우엔 i대신 다른 변수명 사용 */
		int sum = 0;
		int num;
		
		for(num = 0; sum < 100 ; num++) {
			sum += num; // sum = sum + num -> sum을 초기화 하지 않으면 오류가 남.
			System.out.println("num : " + num + " / sum : " + sum);
		}
		
		System.out.println("num : " + num);
		System.out.println("sum : " + sum);

	}
}

 

응용 예제 
public class Ex_02_04 {
	public static void main(String[] args) {
		/* 1부터 100사이의 정수 중에서 3또는 4의 배수의 합을 구하는 코드를 작성 */
		
		int sum = 0;
		for (int i = 1; i <= 100; i++) {
	        if ((i%3==0) || (i%4==0)) {
			sum += i;
	        }
		}
		System.out.println("3 또는 4의 배수의 합 : " + sum );
	}
}

 


 

2)  중첩 for문

 

💡 중첩의 횟수 제한 x.

💡 안쪽 for문의 모든 반복이 끝나고서야 바깥쪽 for문의 다음 반복으로 넘어간다.

 

별 출력
public class Ex_03_03 {
	public static void main(String[] args) {
		/* for문을 이용해서 다음과 같이 *를 출력하는 코드를 작성해보세요. 
		 * 
		 **
		 ***
		 ****
		 */
		
		for (int i=1; i<=4; i++) {
			
			for (int j=1; j<=i; j++) {
				System.out.print("*");
				
			}
			System.out.println();
		}
	}
}

 

구구단 출력
public class MyFor_11 {
	public static void main(String[] args) {
		/* 중첩 for문으로 구구단 출력
		 * for문은 또 다른 for문을 내포할 수 있는데, 이것을 중첩 for문이라고 함.
		 * 이 경우 바깥쪽 for문이 한 번 실핼할 때마다 중첩된 for문은 지정된 횟수만큼
		 * 반복해서 돌다가 다시 바깥쪽 for문이 돌아감.  */
		
		for(int dan = 2; dan <= 9; dan++) { // 바깥 쪽 for문. 8번 반복
			System.out.println("*** " + dan + "단 ***");
			
			for(int times = 1; times <= 9; times++) { // 중첩 for문. 9번 반복
			    System.out.println(dan + " X " + times + " = " + dan * times);
			}
			System.out.println(); // -> 각 단이 끝날 때 넣어줌.
		}
	}
}

 

주사위 게임
public class Ex_03_01 {
	public static void main(String[] args) {
		/* 2개의 주사위를 던지는 게임이 있다고 가정하자.
		 * 중첩 for문을 이용하여 2개의 주사위의 합이 6이 되는 경우를 출력하는 코드 작성.
		   (1,5) (2,4) (3,3) (4,2) (5,1) */
		
		for (int i = 1; i <= 6; i++) {
			
			for ( int j = 1; j <= 6; j++) {
				if (i+j == 6)
				System.out.println("(" + i + ", " + j + ")");
				// 변수 통상적으로 i 다음에 j 씀.
			}
		}
	}
}

 


 

3)  float 타입 카운터 변수

 

  ✓  for문을 작성할 때 주의할 점은 초기화 식에서 루프 카운터 변수를 선언할 때 부동 소수점을 쓰는 float 타입을 사용 x
         ➡️  0.1은 float 타입으로 정확하게 표현할 수 없기 때문에 x에 더해지는 값이 0.1보다 약간 커서, 루프는 9번 실행
         ➡️  float과 double은 계산이 부정확
  ✓  대안으로 정수로 변환 후 계산 결과 값을 실수로 변환 or 자바에서는 정확한 실수 계산을 위해 Decimal 클래스를 제공

public class MyFor_05 {
	public static void main(String[] args) {
		
		for (float x = 0.1f; x <= 1.0f; x += 0.1f) {
			System.out.println(x);
			/* 0.1
			 * 0.2
			 * 0.3
			 * 0.4
			 * 0.5
			 * 0.6
			 * 0.70000005
			 * 0.8000001
			 * 0.9000001 */
		}
	}
}

 


 

4)  for문 동작 시간 구하기


  📍  프로그램의 동작 시간을 구하는 방법은 프로그램 시작 위의 부분에서 시작시간을 구하고, 프로그램이 끝나는 부분에서 종료시간을 구한후 종료시간에서 시작시간을 빼면 프로그램이 동작한 시간을 구할 수 있음

public class MyFor_04 {
	public static void main(String[] args) {
		
	     long startTime = System.currentTimeMillis(); // 시작시간
	     for(int i = 0; i < 1000000000; i++) { // 10억번 반복
	    	 ; // 빈문장 실행
	     }
	     long endTime = System.currentTimeMillis(); // 종료시간
	     
	     System.out.println("시작시간 : " + startTime);
	     System.out.println("종료시간 : " + endTime);
	     System.out.println("소요시간 : " + (endTime - startTime));

	}
}
 

 

 

 

(출처 : 학원강의 및 java의 정석 3rd)


1.   비트 연산자

  • 피연산자를 비트단위로 논리 연산
  • 비트 : 0과 1을 저장 → 1바이트 = 8비트
  • 10진수 / 16진수 / 2진수 변환 참조 (https://reversecore.com/96)
| (OR연산자)
피연산자 중 한 쪽의 값이 1이면 1을 결과로 얻는다. 그 외 0
& (AND연산자)
피연산자 양쪽이 모두 1이어야만 1을 결과로 얻는다. 그 외 0
^ (XOR연산자)
피연산자 값이 서로 다를 때만 1을 결과로 얻는다. 같을때 0
public class Exam011 {

	public static void main(String[] args) {
		
        /* 비트 연산자
         * 10진수를 2진수로 변경해서 연산하고, 그 후에 10진수로 변환
         */
		
		int a = 15; // 1111
		int b = 5; // 0101
		
		System.out.println(a | b); // 1111 -> 15
		System.out.println(a & b ); // 0101 -> 5
		System.out.println(a ^ b ); // 1010 -> 10
		
		System.out.println(a>>2); // 1111 -> 0011 -> 3
		System.out.println(b<<4); // 0101 -> 01010000 -> 80
	}

}

 


2.   제어문 - 조건문

💌  ' 제어문(control statement) '  프로그램의 흐름(flow)을 바꾸는 역할을 하는 문장들

1)  if 문 ; 조건에 따라 다른 문장이 실행

if (조건식) {
    // 조건식이 참(true)일 때 수행될 문장들을 적는다.
}
 
  • 조건식의 결과는 true 아니면 false
  • 연산자들은 조건문에 자주 쓰임.
  • 블럭 {} : 여러 문장을 하나의 단위로 묶을 수 있음

💡  블럭 내의 문장들은 탭(tab)으로 들여쓰기(indentation)를 해서 블럭 안에 속한 문장이라는 것을 알기 쉽게 해주는 것이 좋다

public class Exam015 {
	public static void main(String[] args) {
		
        int a = 3;
        
        if (a >= 3) { // if (조건식) 조건식 : 결과값이 boolean
        	// 조건식이 참일 때 실행
            System.out.println("a는 3보다 큽니다");
        } // if 구문의 끝
        
        // 조건식과 상관없이 무조건 실행
        System.out.println("검사가 끝났습니다.");

	}
}

 


2) if - else 문

 

'else' 그 밖의 다른 이라는 뜻이므로 조건식의 결과가 참이 아닐 때, 즉 거짓일 때 else 블럭의 문장을 수행하라는 뜻

실행할 명령문이 간단하면 삼항연산자로 변경 가능

public static void main(String[] args) {
		
    int age = 15;
		
	if (age > 19) {
	    System.out.println("성인입니다");
		System.out.println("성인요금이 적용됩니다");
	} else { // 위의 조건이 거짓일 때만 실행
	    System.out.println("청소년입니다.");
		System.out.println("청소년요금이 적용됩니다.");
	}
	System.out.println("결제를 진행해 주세요.");
}

 

삼항연산자 적용시 
public static void main(String[] args) {
    /* Exam016을 삼항 연산자를 사용하는 방식으로 변경 
	 * if else 문이고, 실행할 명령문이 간단한 경우 삼항연산자로 변경 가능 */
		
     int age = 15;
		
	 System.out.println(age > 19 ? "성인입니다. \n성인요금이 적용됩니다."
	     : "청소년입니다. \n청소년 요금이 적용됩니다.");
	 System.out.println("결제를 진행해 주세요.");
}


3) if - else if 문

 

  👾  처리해야 할 경우의 수가 셋 이상인 경우 한 문장에 여러 개의 조건식을 쓸 수 있음

  👾  마지막에는 else 블럭으로 끝나며, 위의 어느 조건식도 만족하지 않을 때 수행될 문장들을 적는다.

 

처리 순서 
  • 결과가 참인 조건식을 만날 때까지 첫 번째 조건식부터 순서대로 평가한다.
  • 참인 조건식을 만나면, 해당 블럭 {}의 문장들을 수행한다.
  • if-else if문 전체를 빠져나온다.
public static void main(String[] args) {
    /* 놀이공원의 요금 나이 기준
	* 성인 : 20세 이상
	  청소년 : 14 ~ 19
	  어린이 : 9 ~13
	  유아 : 0 ~8 */
		
    int age;
	   
	Scanner scanner = new Scanner(System.in);
	System.out.println("나이를 입력해주세요. >>");
	age = scanner.nextInt();
	   
	// 조건이 거짓이면 다음 조건으로 넘어감.
	// 조건이 참이 되면 다음 조건을 검사하지 않음.
		
	if (age > 19) { // 20 ~
	    System.out.println("성인입니다.");
		System.out.println("성인요금이 적용됩니다.");
	}
	else if (age > 13) { // 14 ~ 19
		System.out.println("청소년입니다.");
		System.out.println("청소년요금이 적용됩니다.");
	}
	else if (age > 8) { // 9 ~13
		System.out.println("어린이입니다.");
		System.out.println("어린이요금이 적용됩니다.");
	}
	else { // 0~ 8
		System.out.println("유아입니다.");
		System.out.println("유아요금이 적용됩니다.");
	}
		
	System.out.println("결제를 진행해주세요.");
	scanner.close();
}

오름차순으로 나이 적용시
public static void main(String[] args) {
		
    Scanner scanner = new Scanner(System.in);
	int age;
		
	System.out.println("나이를 입력해주세요.");
    age = scanner.nextInt();
        
    /* 놀이공원의 요금 나이 기준
     * 성인 : 20세 이상
     * 청소년 : 14 ~ 19
     * 어린이 : 9 ~ 13
     * 유아 : 0 ~ 8
     */
        
    // 첫번째 조건에 해당 되지 않으면 다음 조건으로 넘어감.
    // 그렇기 때문에 두번째 조건에서 첫번째 조건과 겹치는 범위는 적지 않아도 됨.
        
    if (age <= 8) { // 0~8
        System.out.println("유아입니다.");
        System.out.println("유아요금이 적용됩니다.");
    }
    else if (age <= 13) { // 9 ~ 13
        System.out.println("어린이입니다.");
        System.out.println("어린이요금이 적용됩니다.");
    }
    else if (age <= 19) { // 14 ~ 19
        System.out.println("청소년입니다.");
        System.out.println("청소년요금이 적용됩니다.");
    }
    else { // 20 ~
        System.out.println("성인입니다.");
        System.out.println("성인요금이 적용됩니다.");
    }
        
    System.out.println("결제를 진행해주세요."); 
        
    // 삼항 연산자를 중첩해서 사용하면 동일한 기능 가능.
    System.out.println(age <= 8 ? "유아입니다. \n유아요금이 적용됩니다." :
        age <= 13 ? "어린이입니다. \n어린이요금이 적용됩니다." :
        age <= 19 ? "청소년입니다. \n청소년요금이 적용됩니다." :
         "성인입니다. \n성인요금이 적용됩니다.");
    
    scanner.close();
}
 
 
 

4) 중첩 if문

  👾  if 문 블럭 내에 또 다른 if문을 포함시키는 것

  👾  내부 if 문은 외부의 if 문보다 안쪽으로 들여쓰기를 해서 두 if 문의 범위가 명확히 구분될 수 있도록 작성

public static void main(String[] args) {
    /* 중첩 if문 : if문 안에 if가 있는 경우
		 
	아이디, 비밀번호를 입력받아서 로그인 처리.
	id : java, pw : 1234
	아이디가 맞는 경우에는 비밀번호를 입력 받음.
	아이디가 틀린 경우에는 에러메시지 출력.
	*/
		
	String id, password;
	Scanner scanner = new Scanner(System.in);
	System.out.println("아이디를 입력해 주세요:");
	id = scanner.nextLine();
		 
	if (id.equals("java")) { // 아이디가 맞는 경우 비밀번호를 입력받음
	    System.out.println("아이디 일치");
		System.out.println("비밀번호를 입력해 주세요 : ");
		password = scanner.nextLine();
		if (password.equals("1234")) {
		    System.out.println("비밀번호 일치");
			System.out.println("로그인 성공");
		} else {
			System.out.println("비밀번호 불일치");
		}
	} else {
	    System.out.println("아이디 불일치");
	}
	scanner.close();
}
 

(출처 ; 학원강의 및 java의 정석 3rd)


1.  람다식   Lambda Expression

🚀  람다식은 람다 표현식, 람다라고도 불림
🚀  람다식을 한 줄로 표현하면 '람다식은 마치 값처럼 다룰 수 있는 익명함수'

      ⚡️  람다식을 값처럼 다룰 수 있다는 말은, 람다식 자체가 함수의 인수가 되거나 반환값이 될 수 있다는 의미

    val sayHello = fun() { println("Hello world!") }
    sayHello()

 

1)  람다식 정의

    ✓  람다를 이용하여, 인수 숫자의 제곱값을 반환

    val squareNum: (Int) -> Int = { number -> number * number }
    println(squareNum(12))
  • squareNum : 람다식을 저장할 변수의 이름을 지정
  • (Int) : 람다식의 인수 자료형을 지정
  • Int : 람다식의 반환 자료형을 지정. 이 경우에는 정수를 넣고 정수를 반환.
  • number : 인수 목록을 나열. number의 자료형은 자료형에서 명시해주었으므로 형추론이 되어 number는 Int가 됨.
  • number * number : 람다식에서 실행할 코드를 지정

 

 📌  자료형은 인수목록에서 명시해주어도 됨. 앞의 코드와 동일하게 작동하는 함수

    val squareNum2 = { number: Int -> number * number }
    println(squareNum2(12))

 

📌  람다식의 인수가 한 개이면 인수를 생략하고 it으로 지칭할 수 있음

    val squareNum3: (Int) -> Int = { it * it }
    println(squareNum3(12))

 


    2)  람다를 표현하는 다양한 방법

 람다는 '값처럼' 사용할 수 있는 익명 함수. 값처럼 사용한다는 것은 함수의 인수로도 넣어줄 수 있다.

    fun invokeLambda(lambda: (Int) -> Boolean): Boolean { //람다식을 인수로 받음.
        return lambda(5)
    }
    // 이 함수는 다음과 같이 람다식을 인수로 넣어 사용할 수 있음.
    val paramLambda: (Int) -> Boolean = { num -> num == 10 }
    println(invokeLambda(paramLambda)) // 람다식의 인수로 넣은 5 != 10 이므로 -> false
    // 변수를 사용하지 않고도 바로 넣어줄 수도 있음.
    // 다음의 람다식들은 모두 똑같이 작동
    invokeLambda({ num -> num == 10 }) // 람다식 바로 넣어주기
    invokeLambda({it == 10}) // 인수가 하나일 때 it으로 변경 가능
    invokeLambda(){ it == 10 } // 만약 함수의 마지막 인수가 람다일 경우 밖으로 뺄 수 있음.
    invokeLambda{ it == 10 } // 그 외 인수가 없을 때 () 생략 가능.

 

3)  SAM(Single Abstract Method) 변환

📍  안드로이드를 개발하다 보면 다음과 같은 코드를 아주 많이 작성하게 됨

    button.setOnClickListener {
        // 버튼이 눌렸을 때 작성할 코드
    }

 

  👾  함수의 인수가 하나이고 람다식인 경우에 ()를 생략하고 {}에 코드를 작성할 수 있음
  👾  setOnClickListener() 함수는 람다식이 아닌 OnClickListener 인터페이스를 인수로 받음

    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }


  👾  OnClickListener는 다음과 같이 추상 메서드 하나가 있는 인터페이스

public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }


  ⚡️  setOnClickListener 는 이와 같이 람다식이 아님에도 마치 람다식처럼 취급되고 있음
         ▶️  이것이 가능한 이유가 바로 자바 8에서 소개된 SAM 변환
             

🤓  SAM 변환은 두 가지 조건이 있다

    1. 코틀린 인터페이스가 아닌 자바 인터페이스
    2. 인터페이스 내에는 딱 한 개의 추상 메서드만 존재

    이 조건을 만족하는 경우 익명 인터페이스 객체 생성에 람다식을 사용할 수 있음.
    람다식을 사용하면 코드가 훨씬 간결해지고 가독성이 높아짐.

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  지연 초기화

코틀린은 지연 초기화를 사용하는데 이는 클래스의 코드에 Nullable 처리가 남용되는 것을 방지해 줌

 

1) lateinit

개발을 하다 보면 클래스 안에서 변수(프로퍼티)만 Nullable로 미리 선언하고 초기화(생성자 호출)를 나중에 해야 할 경우가 있는데, 이럴 경우 lateinit 키워드를 사용할 수 있음

Nullable로 선언하는 일반적인 방법


  👾  일반적인 선언 방식으로 처음에 null 값을 입력해두고, 클래스의 다른 메서드 영역에서 값을 입력함

class Person {
    var name: String? = null
    init {
        name = "Jane"
    }
    fun process() {
        name?.plus(" Messi")
        println("이름의 길이 = ${name?.length}")
        println("이름의 첫글자 = ${name?.substring(0, 1)}")
    }
}

 

  📍 이 방식은 변수에 입력된 값의 메서드나 프로퍼티를 사용할 때 Safe Call(?.)이 남용되어 가독성을 떨어트리는 문제가 있음

 

 lateinit을 사용하는 방법


  👾  lateinit을 사용하면 Safe Call을 쓰지 않을 수 있기 때문에 코드에서 발생할 수 있는 수많은 ?를 방지할 수 있음

class Person2 {
    lateinit var name: String
    init {
        name = "Jane"
    }
    fun process() {
        name.plus(" Messi")
        println("이름의 길이 = ${name.length}")
        println("이름의 첫글자 = ${name.substring(0, 1)}")
    }
}

 

 

lateinit의 특징


  ✓  var로 선언된 클래스의 프로퍼티에서만 사용할 수 있음
  ✓  null 은 허용되지 않음
  ✓  기본 자료형 Int, Long, Double, Float 등은 사용할 수 없음

  ✓  lateinit은 변수를 미리 선언만 해놓은 방식이기 때문에 초기화되지 않은 상태에서 메서드나 프로퍼티를 참조하면 null 예외가 발생해서 앱이 종료. 따라서 변수가 초기화되지 않은 상황이 발생할 수 있다면 Nullable이나 빈 값으로 초기화하는 것이 좋음

 


2)  lazy

👩🏻‍💻  lazy는 읽기 전용 변수인 val을 사용하는 지연 초기화
👩🏻‍💻  lateinit이 입력된 값을 변경할 수 있는 반면, lazy는 입력된 값을 변경할 수 없음
👩🏻‍💻  val로 변수를 먼저 선언한 후 코드의 뒤쪽에 by lazy 키워드를 사용, 그리고 by lazy 다음에 나오는 중괄호 {}에 초기화할 값을 써주면 됨

class Company {
    // by lazy를 사용하면 반환되는 값의 타입을 추론할 수 있기 때문에 변수의 타입을 생략할 수 있음.
    val person: Person by lazy { Person() }
    init {
        // lazy는 선언 시에 초기화 코드를 함께 작성 하기 때문에 초기화 과정이 필요없음.
    }
    fun process() {
        println("person은 이름은 ${person.name}") // 최초 호출하는 시점에 초기화.
    }
}

 

lazy의 특징


  ✓  선언 시에 초기화 코드를 함께 작성하기 때문에, 따로 초기화 할 필요가 없음
  ✓  lazy로 선언된 변수가 최초 호출되는 시점에 by lazy{} 안에 넣은 값으로 초기화
  ✓  코드에서 Company 클래스가 초기화 되더라도 person에 바로 Person()으로 초기화 되지 않고, process() 메서드에서 person.name이 호출되는 순간 초기화

💡  lazy는 주의해서 사용. 지연 초기화는 말 그대로 최초 호출되는 시점에 초기화 작업이 일어나기 때문에 초기화하는데 사용하는 리소스가 너무 크면 (메모리를 많이 쓰거나 코드가 복잡한 경우) 전체 처리 속도에 나쁜 영향을 미칠 수 있음

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.   제네릭

제네릭 Generics은 입력되는 값의 타입을 자유롭게 사용하기 위한 도구

📌 다음은 실제 MutableList의 선언부

public interface MutableList<E> : List<E>, MutableCollection<E>


  ✓  클래스명 옆에 <E>부분에 String 같은 특정 타입이 지정되면 클래스 내부에 선언된 모든 E에 String 타입으로 지정
  ✓  결과적으로 var list: Array<E>가 var list: Array<String>으로 변경이 되는 것

  ⚡️  이렇게 설계된 클래스는 주로 구현하는 용도로 사용하며 컬렉션이나 배열에서 입력되는 값의 타입을 특정하기 위해 다음과 같이 사용

var list: MutableList<String> = mutableListOf("abc", "def", "ghi") 
fun testGenerics() {
    // String으로 제네릭을 사용했기 때문에 list 변수에는 문자열만 담을 수 있음.
    var list: MutableList<String> = mutableListOf()
    list.add("abc")
    list.add("def")
    list.add("ghi")
//    list.add(30) // 입력 오류가 발생

    // String 타입의 item 변수로 꺼내서 사용할 수 있음.
    for (item in list) {
        println("리스트에 저장된 값은 ${item}입니다")
    }
}

 


2.  Null

코틀린은 Null 값의 처리에 많은 공을 들인 언어. null은 프로그래밍하면서 항상 이슈의 중심에 있는데 null로 인해 프로그램 전체, 혹은 앱 자체가 멈출 수 있기 때문.

 

 프로그램이 멈출 수 있는 상황 예시

   

      main() 함수 안에 one 변수를 하나 선언하고 타입으로 클래스를 지정.
     그리고 특정 조건이 만족할 때만 선언한 변수에 생성자를 호출해서 저장하는 조건문 if를 만듦.
     그리고 변수를 통해 해당 클래스의 메서드를 호출.

class One {
    fun printMe() {
        print("null safety")
    }
}

fun main() {
    var one: One
    if (1 > 2) {
        one = One()
    }
 //   one.printMe()
}

 

  👾  이 코드에서 조건이 false이기 때문에 one 변수는 아무것도 없는 null 상태가 됨
         ➡️  print 메서드를 호출하면 null 포인터 예외가 발생하면서 프로그램이 다운, 물론 IDE에서 오류를 발생시켜 컴파일되지 않도록 막아줌
  👾  코드양이 많아 지면 이런 상황이 언제든지 발생할 수 있는데, 코틀린은 이런 상황을 방지하기 위해서 안전장치를 마련해둠
         ➡️  그 결과물이 Null Safety


1)  null 값 허용하기 : ?


코틀린에서 지정하는 기본 변수는 모두 null이 입력되지 않음
null 값을 입력하기 위해서는 변수를 선언할 때 타입 뒤에 ? (Nullable, 물음표)를 입력.

var variable : String?

 

 

변수에 null 허용하기

변수의 타입 뒤에 물음표를 붙이지 않으면 null 값을 입력할 수 없음.
null 예외를 발생시키고 싶지 않다면 기본형으로 선언.
fun main() {
    var nullable: String? // 타입 다음에 물음표를 붙여서 null 값을 입력할 수 있음.
    nullable = null

    var notNullable: String
//    notNullable = null // 일반 변수에는 null을 입력할 수 없음.
}

함수 파라미터에 null 허용 설정하기

함수의 파라미터에도 null 허용 여부를 설정할 수 있음.
함수의 파라미터가 null을 허용하려면 해당 파라미터에 대해서 null 체크를 먼저 해야만 사용할 수 있음.
단, 파라미터를 조건문에서 null인지 아닌지를 체크해야만 사용할 수 있음
fun nullParameter(str: String?) {
    // 파라미터 str에 null이 허용되었기 때문에 함수 내부에서 null 체크를 하기 전에는 str을 사용할 수 없음.
    if (str != null) {
        var length = str.length
    }
}

함수의 리턴 타입에 null 허용 설정하기

함수의 리턴 타입에도 물음표를 붙여서 null 허용 여부를 설정할 수 있음.
함수의 리턴 타입에 Nullable이 지정되어 있지 않으면 null 값을 리턴할 수 없음.
fun nullReturn(): String? {
    return null
}

 

2)  안전한 호출 : ?.


변수를 Nullable로 만들기 위해서 물음표를 사용.
?.(Safe Call, 물음표와 점)을 사용하면 null 체크를 간결하게 할 수 있음
  ✓  Nullable인 변수 다음에 ?.을 사용하면 해당 변수가 null일 경우 ?. 다음의 메서드나 프로퍼티를 호출하지 않음

fun testSafeCall(str: String?): Int? {
    // 문자열 길이를 반환하는 length 프로퍼티를 호출했는데 str 변수 자체가 null일 경우는 
    // length 프로퍼티를 호출하지 않고 null을 반환.

    var result: Int? = str?.length
    return result
}

3)  Null 값 대체하기 : ?:


?:(Elvis Operator, 물음표와 콜론)을 사용하면 원본 변수가 null 일 때 넘겨줄 기본값을 설정할 수 있음

 

다음 코드에서 Safe Call 다음에 호출되는 프로퍼티 뒤에 다시 ?:을 붙이고 0이라는 값을 표시.
이렇게 호출하면 str 변수가 null일 경우 가장 뒤에 표시한 0을 반환.
fun testElvis(str: String?): Int {
    // length 오른쪽에 ?:을 사용하면 null일 경우 ?: 오른쪽의 값이 반환.
    var resultNonNull: Int = str?.length ?: 0
    return resultNonNull
}

 


 

💫  물음표의 위치와 형태에 따라서 Nullable, Safe Call, Elvis Operator가 구분

🤓  Nullable

표기법 : 선언하는 변수의 타입 다음에 ? 표기.
사용 목적 : null을 입력받기 위해 사용.
사용 예 : var nullable: String?
🥸  Safe Call

표기법 : 선언된 변수의 이름 다음에 ?. 표기.
사용 목적 : null 일 때 ?. 다음에 나오는 속성이나 명령어를 처리하지 않기 위해 사용.
사용 예 : var result = 변수?.length
🧐  Elvis Operator

표기법 : 선언된 변수의 이름 다음에 ?: 표기.
사용 목적 : null일 때 ?: 다음에 나오는 값을 기본 값으로 사용.
사용 예 : var result = 변수 ?: 0

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1. 접근 제한자

코틀린에서 정의되는 클래스, 인터페이스, 메서드, 프로퍼티는 모두 접근 제한자 Visibility Modifiers를 가질 수 있음.
함수형 언어라는 특성 때문에 코틀린은 기본 객체지향에서 접근 제한자의 기준으로 삼았던 패키지 대신에 모듈 개념이 도입

💡 코틀린에서 모듈이란?
    -  코틀린에서 모듈이란 한 번에 같이 컴파일되는 모든 파일을 말함.
        안드로이드를 예로 든다면 하나의 앱이 하나의 모듈이 될 수 있음. 또한 라이브러리도 하나의 모듈.


1) 접근 제한자의 종류


  🐰  접근 제한자는 서로 다른 파일에게 자신에 대한 접근 권한을 제공하는 것인데 각 변수나 클래스 이름 앞에 아무런 예약어를 붙이지
않았을 때는 기본적으로 public 접근 제한자가 적용

 

각각의 접근 제한자가 공개하는 범위
  • public : 코틀린의 기본 접근 제한자. 어디서나 접근 가능
  • internal : 같은 모듈 내에서 접근 가능. 안드로이드 개발 시에는 한 프로젝트 안에 있으면 같은 모듈
                    만약 한 프로젝트에 여러 모듈을 만든다면 이는 모듈 간 접근이 제한
  • protected : 자식 클래스에서는 접근할 수 있음
  • private : 해당 클래스 내부에서만 접근할 수 있음

2) 접근 제한자의 적용


  🐰  접근 제한자를 붙이면 해당 클래스, 멤버 프로퍼티 또는 메서드에 대한 사용이 제한

open class Parent {
    private val privateVal = 1
    protected open val protectedVal = 2
    internal val internalVal = 3
    val defaultVal = 4
}

class Child: Parent() {
    fun callVariables() {
 //       println(privateVal) // 호출이 안됨.
        println(protectedVal) // 상속 관계이므로 접근할 수 있음.
        println(internalVal)  // 동일한 모듈이므로 접근할 수 있음
        println(defaultVal)
    }
}

// 상속 관계가 아닌 외부 클래스에서 Parent 클래스를 생성하고 사용.
// 상속 관계가 아니기 때문에 public과 internal 에만 접근할 수 있음
class Stranger {
    fun callVariables() {
        val parent = Parent()
//        println(parent.privateVal) // 호출이 안됨.
//        println(parent.protectedVal)  // 호출이 안됨.
        println(parent.internalVal)
        println(parent.defaultVal)
    }
}

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts