코트카타
21) 하샤드 수
나의 풀이
import java.util.*;
import static java.lang.Integer.parseInt;
class Solution {
public boolean solution(int x) {
boolean answer = true;
String strX = ""+x;
int[] arrX = new int[strX.length()];
int sum=0;
for(int i=0;i<strX.length();i++){
arrX[i]=Integer.parseInt(String.valueOf(strX.charAt(i)));;
}
for(int i=0;i<arrX.length;i++){
sum += arrX[i];
}
answer = (x%sum)==0?true:false;
return answer;
}
}
x를 String으로 만들어서
그 String의 크기만큼 새로운 int 배열을 만들었다.
각각의 인덱스에 자릿수 값을 할당한다.
그리고 각 자릿수를 더해서
x를 sum으로 나눈 나머지가 0이면 true아니면 false를 반환한다.
다른 사람의 풀이
class Solution {
public boolean solution(int x) {
int sum = String.valueOf(x).chars().map(ch -> ch - '0').sum();
return x % sum == 0;
}
}
출처:https://school.programmers.co.kr/learn/courses/30/lessons/12947/solution_groups?language=java
스트림에 대해 익숙해지고자 이 풀이를 더 살펴보기로 했다.
String.valueOf(x)
: 숫자 x를 문자열로 변환
예) x = 123 → "123"
.chars()
: 문자열의 각 문자를 Unicode 값(ASCII 값)으로 스트림으로 변환
예) "123".chars() → [49, 50, 51] (각 숫자 문자 '1', '2', '3'의 Unicode 값)
.map(ch -> ch - '0')
: 스트림의 각 Unicode 값에서 문자 '0'의 Unicode 값을 뺌
이를 통해 문자 '9'를 정수 9로 변환가능
예: '1' - '0' → 1, '2' - '0' → 2, '3' - '0' → 3
.sum()
: 변환된 숫자 스트림의 합을 구함
예: [1, 2, 3].sum() → 6
팀원분의 특강 :)
팀원분께서 우리 팀에서 레벨 3 과제에 대해 매우 어려워하는 것을 보시고
특강이라는 큰 도움을 주셨습니다. :)
김영한님 자바 강의를 참고하셨다고 합니다.
저의 경우에는 구글링을 통해 일부 과제를 수행했지만
정확한 개념 정리가 잘 되지 않던 터라 정말 반가웠습니다.
제네릭
먼저 왜 써야 하는지? 생각해봐야 합니다.
아래의 예시를 통해 살펴보겠습니다.
public class IntegerBox {
private Integer value;
public Integer get() {
return value;
}
public void set(Integer value) {
this.value = value;
}
}
Tip) cmd+shift+Enter : 자동완성
public class StringBox {
private String value;
public String get() {
return value;
}
public void set(String object) {
this.value = object;
}
}
이렇게 각각 정수와 문자열을 보관하고 가져올 수 있는 클래스를 만들었습니다.
public class Main {
public static void main(String[] args) {
IntegerBox integerBox = new IntegerBox();
integerBox.set(10); //오토 박싱
Integer integer = integerBox.get();
System.out.println("integer = " + integer);
StringBox stringBox = new StringBox();
stringBox.set("hello");
String str = stringBox.get();
System.out.println("str = " + str);
}
}
정수 타입의 값을 IntegerBox에
문자열 타입의 값을 StringBox에 각각 넣어서 출력해보았습니다.
문제점
`Double` , `Boolean`등 타입만 바뀌었을 뿐 메서드 등이 동일한 이 클래스를 재사용하는 방안은 없을까요?
`DoubleBox` , `BooleanBox` 와 같이 클래스를 계속 새로 만들어야 할까요?
1차 해결
상속, 다형성 개념 이해가 필요합니다.
모든 타입을 다 받을 수 있는 Object 타입으로 Box를 만들어 줍니다.
public class ObjectBox {
private Object value;
public Object get() {
return value;
}
public void set(Object object) {
this.value = object;
}
}
public class Main {
public static void main(String[] args) {
ObjectBox integerBox = new ObjectBox();
integerBox.set(10);
Object obj = integerBox.get(); // 에러
Integer integer = (Integer) integerBox.get(); //Object -> Integer 캐스팅
System.out.println("integer = " + integer);
ObjectBox stringBox = new ObjectBox();
stringBox.set("hello");
String str = (String) stringBox.get(); //Object -> String 캐스팅
System.out.println("str = " + str);
//잘못된 타입의 인수 전달시
integerBox.set("문자100");
Integer result = (Integer) integerBox.get(); // String -> Integer 캐스팅 예외
System.out.println("result = " + result);
}
}
이렇게 Object타입을 활용할 경우, 자식은 부모를 담을 수 없기 때문에
어떤 타입으로 사용할지 강제형변환을 꼭 해야 합니다.
잘못된 타입의 인수를 전달할 시, 컴파일상에서는 문제를 잡아내지 못합니다.
따라서 컴파일 에러가 아닌 실행 시 에러가 발생합니다.
컴파일상에서 에러를 잡을 수 있는 것이 더 좋기 때문에
Object타입을 사용하는 것은 바람직하지 않습니다.
=> 재사용성 good, 타입 안정성 bad
2차 해결
따라서 컴파일상에서 타입 불일치 오류를 잡아낼 수 있는 제네릭을 사용합니다.
보통 T를 많이 사용하는데, 여기에서는 이해하기 쉽게 Type이라고 임의로 사용하겠습니다.
public class GenericBox<Type> {
private Type value;
public Type get() {
return value;
}
public void set(Type value) {
this.value = value;
}
}
public class Main {
public static void main(String[] args) {
GenericBox<Integer> integerBox = new GenericBox<Integer>(); //생성 시점에 T의 타입 결정
integerBox.set(10);
//integerBox.set("문자100"); // Integer 타입만 허용, 컴파일 오류
Integer integer = integerBox.get(); // Integer 타입 반환 (캐스팅 X)
System.out.println("integer = " + integer);
GenericBox<String> stringBox = new GenericBox<String>();
stringBox.set("hello"); // String 타입만 허용
String str = stringBox.get(); // String 타입만 반환
System.out.println("str = " + str);
}
}
위와 같이 제네릭 클래스를 활용하면, <> 꺽쇠 안에 다양한 타입을 넣어서 다양하게 확장 가능합니다.
(이에 대한 예시를 우리 팀은 대체로 게임을 좋아해서 게임을 예시로 들어주셨다.)
먼저 아무 팀이나 나타낼 수 있는 Team 클래스를 만들었습니다.
public class Team {
private Team teamName;
public String getTeamName(){
return "익명팀"
}
}
public class T1 extends Team {
private String teamName;
@Override
public String getTeamName(){
return "T1"
}
}
public class Hwe extends Team {
private String teamName;
@Override
public String getTeamName(){
return "한화생명"
}
}
T1과 Hwe는 각각 Team 클래스를 상속받아서 오버라이딩 합니다.
public class Fan{
private Team team;
public Fan(Team team){
this.team = team;
}
public void cheer(){
System.out.println("화이팅" + team.getTeamName();)
}
}
팬 클래스도 만듭니다.
팀 객체를 받아서 각 팀을 응원할 수 있는 메서드도 만듭니다.
public class Main {
public static void main(String[] args) {
Team t1 = new T1(); //부모는 자식을 품을 수 있다. 다형성 Integer->Obeject같이 상위
//T1 team = new Team(); // 에러 자식은 부모를 품을 수 없다.
Fan t1Fan = new Fan(t1);
t1Fan.cheer();
// 설정한 메서드 말고도 다른 메서드 사용가능한 이유
// 내부적으로 Object를 상속받기 때문
}
}
만약 제네릭을 사용한다면?
public class Fan<Type>{
private Type team;
public Fan(Type team){
this.team = team;
}
public void cheer(){
System.out.println("화이팅" + team.getTeamName();)
}
}
위와 같은 경우 모든 타입이 가능하니까 내부적으로는 Object로 만들어집니다.
Object타입에서는 team.getTeamName()를 사용할 수 없습니다.
따라서 Team과 그 자손만 가능하도록 범위를 제한해야 합니다.
그래야 공용 메서드를 사용 가능합니다.
<Type> -> Object, Integer, Double... 다 가능
<Type extends Team> -> 적어도 Team 밑에만 넘어올 수 있습니다.
public class Fan<Type extends Team>{
private Type team;
public Fan(Type team){
this.team = team;
}
public void cheer(){
System.out.println("화이팅" + team.getTeamName();)
}
}
public class Main {
public static void main(String[] args) {
T1 t1 = new T1();
Fan<T1> t1Fan = new Fan<>();
t1Fan.cheer();
}
}
이로써 제네릭을 사용하면 재사용성도 good, 타입 안정성도 good이 됩니다.
추가> 다형성 개념 정리
Team t1 = new T1();
t1은 T1, Team 객체도 생성됩니다.
따라서 무조건 선언한 부모인 Team 타입을 따라 갑니다.
그런데 T1에서 오버라이드한 것이 있다면 T1의 것을 사용합니다.
Enum
왜 사용해야 할까요?
아래의 예시를 통해 살펴보겠습니다.
public Calss AuthService{
public String authCheck(String authGrade) {
if (authGrade.equals("ADMIN")) {
return "관리자 화면";
} else if (authGrade.equals("LOGIN")) {
return "메인 화면";
} else if (authGrade.equals("GUEST")){
return "로그인 화면";
} else {
return null;
}
}
}
관리자면 관리자 화면을, 회원이면 메인화면을, 일반 방문자면 로그인 화면을 보여주도록 처리하는 클래스를 만듭니다.
public class Main {
public static void main(String[] args) {
AuthService as = new AuthService();
String s = as.authCheck("VIP");
String s1 = as.authCheck("ADMIN");
String s2 = as.authCheck("GUEST");
String login = as.authCheck("login");
}
}
이럴 경우에 일반 신입 개발자는 내부정보(ADMIN, GUEST, LOGIN 사용한다는 사실)를 모르므로
그저 String 타입 형식만 지키면 되는 것으로 생각하고 VIP, long을 설정합니다.
1차해결 - 상수
public Calss AuthGrade{
public static final String ADMIN = "ADMIN";
public static final String LOGIN = "LOGIN";
public static final String GUEST = "GUEST";
}
위와 같은 문제를 해결하기 위해 상수라는 개념을 생각해보았습니다.
그러나 결국 상수로 지정해도, 신입 개발자는 내부정보(ADMIN, GUEST, LOGIN 사용한다는 사실)를 모르므로
그저 String 타입으로 입력하는 실수가 발생합니다.
public Calss AuthGrade{
private String grade;
public static final AuthGrade ADMIN = new AuthGrade("ADMIN");
public static final AuthGrade LOGIN = new AuthGrade("LOGIN");
public static final AuthGrade GUEST = new AuthGrade("GUEST");
public AuthGrade(String grade){
this.grade = grade;
}
}
String타입을 AuthGrade로 지정해주었습니다.
위와 같이 사용하면 미리 정한 3개의 상수가 아닌 다른 값을 입력했을 때, 컴파일 단계에서 에러가 발생합니다.(빨간줄)
이를 통해 신입 개발자는 문제점을 확인할 수 있습니다.
public enum AuthGrade {
private final String grade;
ADMIN("ADMIN"),LOGIN("LOGIN"),GUEST("GUEST");
public AuthGrade(String grade){
this.grade = grade;
}
}
마지막으로 아래와 같이 말씀해주셨습니다.
1. 왜 써야 하는지?
2. 이런 상황 발생 시 무엇을 쓸지 판단하기
과제 이해하기
Generic
public class ArithmeticCalculator<T extends Number>{}
int와 같은 정수나 double과 같은 실수 값을 받아오기 위해서는
String과 같은 숫자가 아닌 타입은 받아오지 못하도록 제한한다.
Enum
public enum OperatorType {
PLUS('+'), MINUS('-'), MULTIPLY('*'),DIVIDE('/');
//값을 저장할 필드(인스턴스 변수)를 추가
private final char value;
// 생성자를 추가
private OperatorType(char value){this.value = value; }
public char getValue(){return value;}
// 매개변수가 null이 아니고 enum의 값이 매개변수와 같으면 enum의 값을 리턴하라
public static OperatorType findByVal(char val) {
OperatorType[] values = OperatorType.values();
for (OperatorType v : values) {
if (val==v.value) {
return v;
}
}
return null;
}
}
OperatorType.values()
: 모든 enum 값을 반환
배열로 만들어서 확장된 for문을 사용하여 각각의 요소를 출력할 수 있음
매개변수 val의 값과 enum의 value의 값이 같으면 enum 상수를 반환한다.
추가로 알아두면 좋을 enum 메서드
OperatorType.valueOf()
String input = "+";
OperatorType plus = OperatorType.valueOf(input)
String -> ENUM 변환,
잘못된 문자인 경우, IllegalArgumentException 발생한다.
그래서 내가 과거에 해당 메서드를 사용했을 때 위와 같은 오류가 발생했다.
참고:https://codingtrip.tistory.com/83
'CODING > 스파르타 내일배움캠프 TIL' 카테고리의 다른 글
20_두 정수 사이의 합_개인 과제 정리_튜터님 특강_25.1.9(목) (0) | 2025.01.09 |
---|---|
계산기 개인과제 관련 트러블슈팅 TIL 모음 (0) | 2025.01.09 |
18_정수 내림차순으로 배치하기_개인 과제 진행 Lv3 도전_25.1.7(화) (0) | 2025.01.07 |
17_정수 제곱근 판별_개인 과제 진행 Lv3 도전_25.1.6(월) (0) | 2025.01.06 |
16_코트카타 문자열을 정수로 바꾸기_개인 과제 진행_25.1.3(금) (2) | 2025.01.03 |